Sunday 5:30am fun with XHR, XSS, HTTPS and (session) cookies September 8, 2013Posted by CK in Software.
Tags: Angular, Cookies, CORS, HTTP, HTTPS, XHR, XSS
add a comment
Ok at this time of the day there’s not much energy left, but having spent the day trying to figure this out, I decided to take a few minutes and write about it.
The use case is as follows:
- Browser is on HTTP page, instructed to make XHR to foo.example.com over HTTPS to authenticate (end goal to receive session cookie)
- Browser continues making all other requests to bar.example.com over HTTP using this session cookie.
Seems like business as usual. Same 2nd-level domain, should just work. Not really. Steps to take if you find yourself in the situation:
You will find that due to XSS and browsers setting extra request headers, or maybe because you are using application/xml or application/json content type in a POST request, you will probably need to add CORS support to your backend: Browsers will issue a pre-flight request with the OPTIONS verb. You can add the following headers to your response to this OPTIONS request:
Access-Control-Allow-Methods: GET, POST,
Check if you need to allow headers other than X-Requested-With, but usually this should work for the specific task. Also, you may want to set Access-Control-Max-Age too. These response headers will allow the browser to continue with the GET or POST to authenticate.
It was an unpleasant surprise to see that, although a session cookie was returned successfully after enabling CORS, the browser would ignore it in followup XHRs over HTTP. It turns out there are two more things you need to do:
- Add withCredentials: true in your XHRs (not sure if only the one over HTTPS needs to have it or every one, including followup over HTTP, I used it everywhere for now)
- Add Access-Control-Allow-Credentials: true in your CORS setup from above.
Bonus: If you are using Angular.js you will need to put withCredentials configuration in the $http config object for 1.0.x, otherwise you can set it permanently on $httpProvider defaults for v1.1.1+ — see here.