elasticsearch-head
elasticsearch-head copied to clipboard
Ability to link to "Any Request" with a specific query.
Mainly for debugging purposes in other applications, it would be useful to generate links containing a query to run. For instance, on a product page on an ecommerce site, in development having a link that would link to the "_explain" for the item and the last query run. On the search results page, links to "_analyze" and "_search" would make debugging queries easier.
In order to prevent webpages from reading a developers local ES instance, a timestamp and an HMAC of the query could be added to the request. Since each request would open a new window and hit the server before loading the front-end JS UI, the server could verify the expiration and HMAC and return a 400 error if it is bad. The HMAC could be defined as hmac('sha1', json_encode([querystring.tab, querystring.method, querystring.endpoint, querystring.query, querystring.exires])). (json_encode to prevent concatenation attacks.)
An example request would be http://127.0.0.1:9200/_plugin/head/?tab=AnyRequest&method=POST&endpoint=/products&query={}&expires=0&hmac=abcRF==
The desired outcome would be to open the Any Request tab and and automatically fill in the appropriate form fields and then run the query.
Hi I have been giving this implementation some thought. The concern is that the HMAC is entirely calculable based on things the attacker knows. Consider this scenario;
I (a disgruntled ex-employee) wants to create a new admin user for myself on your elasticsearch cluster. I send you a disguised link. Since I know the IP address - I can craft a url that would perform an action on your elasticsearch cluster, when you click the link, it opens elasticsearch head and immediately executes the query, adding me as an admin on your site.
Here is the details on this type of exploit
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
The classic way of preventing CSRF is to have a secret csrf_token shared between the browser and server which must be submitted with each request. Since elasticsearch and head do not have a trusted relationship of this kind it does not work.
Another way of dealing with this is only allowing non-mutating requests (eg GET only), however you specifically want to allow POST, etc. Limiting the queries to _explain or _analyze could be effective here.
Another solution may be to pre-populate the query, but the user must then click a button to execute the query. I'm not totally sure this is a good idea since it's open to social engineering perhaps.
Another solution may be to have shared secret token (so that the csrf_token can be trusted from remote sources (via configuration), so that a trust relationship between the url generator and the 'head client exists
HMACs are computed with a secret that would be shared between the application generating the link and the head plugin. A third party wouldn't know this value. (I did leave it off my pseudocode in all fairness. It's HMAC(hash function, body, key).
CSRF tokens are normal not request specific and only prevent someone from submitting a form before they load it. The application and head plugin would need someway of verifying the value, and that's why I proposed an HMAC: verification depends on a shared key that isn't available anywhere except inside the application and the head plugin.
Sorry, I can't find an edit on mobile. I agree with you that a CSRF token is a good paradigm to use. I think generating them via an HMAC is the most straight forward and accessible-in-any-langue way of building one. Care given to not concat values to compute the HMAC is important as well.
Ok, I think this is viable. We would need a way of passing the secret to the browser - at the moment 'head still runs as a static app or in standalone. We probably don't need to consider the chrome plugin.
perhaps the app try to load a file called query_token.json in the root directory.
{ eshead_query_token: 0 }
if the file exists and the value is non-zero the queries can be executed as long as the hmac validates?
Alternatively it could look for a cookie value which would have to be set by whatever is serving 'head.
What do you think?
The browser doesn't need to know the secret, only the HMAC for the query using the secret. My app would generate the link and the HMAC (and add the HMAC to the link URL) and place that on the page. When I click on the link head opens, validates that the URL against the HMAC (it computes the HMAC for the query it sees and the secret it knows and then compares its HMAC against the one in the URL).
When the link is click a new tab/window will open with head -- I'm not sure how to get it to open a new tab within an open head already -- which is why I was thinking it could be done all server side.
Again, I can't seem to edit on mobile. I think I misunderstood you and so you can disregard the first paragraph in my previous reply.
I see what you're saying now, getting the token into head's js. If we don't want to/can't do it server side, I would worry about someone malicious fetching that URL to get the token.
I guess I was under the impression that some part of head was executed on the server and then the web app portion of it loaded and that it wasn't just an HTML-only app. Would you be open to having that server-side component doing the validation? I'm not sure how we could re-use a head instance that would need to validate the HMAC without going through the server. (Maybe I'm not being creative enough?)