Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CORS With Basic Authentication #9063

Closed
erikringsmuth opened this issue Dec 24, 2014 · 12 comments
Closed

CORS With Basic Authentication #9063

erikringsmuth opened this issue Dec 24, 2014 · 12 comments
Labels
:Core/Infra/REST API REST infrastructure and utilities discuss feedback_needed

Comments

@erikringsmuth
Copy link
Contributor

Elasticsearch populates the Access-Control-Allow-Headers header on CORS requests here in the NettyHttpChannel. This works as long as you don't send Basic Authentication credentials in the Authorization header.

A CORS preflight request with Basic Authentication credentials looks like this.

OPTIONS https://domain2:9200/_nodes/stats?all=true HTTP/1.1
Host: domain2:9200
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://domain1:80
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
Access-Control-Request-Headers: authorization
Accept: */*
Referer: http://domain1:80/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,et;q=0.6

response

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://domain1:80
Access-Control-Max-Age: 1728000
Access-Control-Allow-Methods: OPTIONS, HEAD, GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Requested-With, Content-Type, Content-Length
Access-Control-Allow-Credentials: true
Content-Type: text/plain; charset=UTF-8
Content-Length: 0

The browser blocks the actual request from happening because Authorization is not in the Access-Control-Allow-Headers header.

There is a workaround by setting this in elasticsearch.yml.

http.cors.allow-headers: "X-Requested-With, Content-Type, Content-Length, Authorization"

This works but isn't ideal. I think the best solution would be for elasticsearch to look at the request's Access-Control-Request-Headers header and dynamically populate the response Access-Control-Allow-Headers with a whitelisted set of headers.

In this case, the request header Access-Control-Request-Headers: authorization should create a response header Access-Control-Allow-Headers: authorization.

If you think this is overkill I'll just stick with setting http.cors.allow-headers in elasticsearch.yml.

@clintongormley clintongormley added :Core/Infra/REST API REST infrastructure and utilities discuss v1.5.0 labels Dec 29, 2014
@clintongormley
Copy link
Contributor

@spinscale @bleskes what are your thoughts on this?

@bleskes
Copy link
Contributor

bleskes commented Jan 5, 2015

@erikringsmuth the w3c spec for CORS are a bit ambiguous about whether the authorization header needs to be specified in the A.C.R.H. header. Because it is a header, one would assume it needs to be there, but on the other hand the explicitly say that Access-Control-Allow-Credentials header controls whether user credentials are allowed and it works for me with Chrome (which you seems to be using) on windows without it. Can you elaborate a bit more about what you did to make it fail without it? a demo html page which fails will be great.

@erikringsmuth
Copy link
Contributor Author

Here are the steps to reproduce what I'm seeing. You don't even need the server checking basic auth credentials. Simply adding them to the request will cause the browser (Chrome) to fail the request.

  1. Start with a fresh install of elasticsearch
  2. Set these in elasticsearch.yml
http.cors.enabled: true
http.cors.allow-origin: "/.*/"
http.cors.allow-credentials: true
  1. Start elasticsearch
  2. Open Chrome. I'd suggest using incognito mode since preflight requests are cached as soon as they succeed once.
  3. Navigate to a non-HTTPS domain other than localhost. A new tab won't work since it's HTTPS. http://example.com should work.
  4. Open the console and try to make this request.
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://localhost:9200/_search', true);
xhr.withCredentials = true;
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Basic ' + btoa('test:test'));
xhr.send('{"query":{"match_all":{}}}');

It should fail with an error like this.

XMLHttpRequest cannot load http://localhost:9200/_search. Request header field Authorization is not allowed by Access-Control-Allow-Headers.
  1. Stop elasticsearch.
  2. Add this setting to elasticsearch.yml.
http.cors.allow-headers: "X-Requested-With, Content-Type, Content-Length, Authorization"
  1. Start elasticsearch and repeate the previous request. It will succeed this time.

Going back to my initial suggestion, parsing Access-Control-Request-Headers to dynamically populate Access-Control-Allow-Headers may be overkill. The simplest thing would be to add Authorization to the default list of Access-Control-Allow-Headers.

@bleskes
Copy link
Contributor

bleskes commented Jan 6, 2015

I see. If you indeed manually add the authorization header it doesn't work any more (remove it and the browser will respond to the 401 response code and popup a credentials window, after which every thing works without the authorization header).

To make sure I understand what you are suggesting changing the semantics of http.cors.allow-headers to be a whitelist rather then a complete fixed list, so that ES will only echo requested headers, if allowed, but not output unneeded ones. Correct?

@erikringsmuth
Copy link
Contributor Author

The credentials window will pop up as long as you're on the same domain but a cross-origin AJAX request won't create the popup.

I think there are two options for the change.

The simplest change would just be adding Authorization to the hard-coded list of allowed headers.

The other option would be to parse the incoming Access-Control-Request-Headers header and use it to dynamically populate the Access-Control-Allow-Headers header. This would probably still require a whitelist of allowed headers or a blacklist of disallowed headers.

Either way works. I don't think there are that many headers elasticsearch cares about so the simplest solution may be the best one for now.

@bleskes
Copy link
Contributor

bleskes commented Jan 6, 2015

The credentials window will pop up as long as you're on the same domain but a cross-origin AJAX request won't create the popup.

for what it's worth, it does pop up for me.

The simplest change would just be adding Authorization to the hard-coded list of allowed headers.

Do you actually use the autherization header in ES through a plugin? It feels a bit odd for me to add a header we don't use nor care about. If you have some proxy in front of ES, I would think that that is proper place for adding the header.

@erikringsmuth
Copy link
Contributor Author

Yeah, I'm using Basic Auth through a plugin. Maybe it's best to keep the headers as is and configure them with http.cors.allow-headers.

@bleskes
Copy link
Contributor

bleskes commented Jan 8, 2015

OK. Thanks for clarifying!

@rileytg
Copy link

rileytg commented Jan 17, 2017

wouldn't it be easier to just include this in the headers?

i solved my issue by finding this thread. having them built in kinda just makes more sense. is there a downside im not seeing?

@ashutoshningot
Copy link

ashutoshningot commented Mar 19, 2018

pre-flight request is giving: 200
CORS ERROR: Reson "kbn-version and content-type missing".
HOW TO PROCCED?

@tvernum
Copy link
Contributor

tvernum commented Mar 20, 2018

@ashutoshningot, Could you please ask you question on our discuss forum where we can provide better support. Those forums are designed to support the kinds of conversations that are necessary for this sort of troubleshooting and problem diagnosis.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:Core/Infra/REST API REST infrastructure and utilities discuss feedback_needed
Projects
None yet
Development

No branches or pull requests

6 participants