jump to navigation

Sunday 5:30am fun with XHR, XSS, HTTPS and (session) cookies September 8, 2013

Posted by CK in Software.
Tags: , , , , , ,
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:

  1. Browser is on HTTP page, instructed to make XHR to foo.example.com over HTTPS to authenticate (end goal to receive session cookie)
  2. 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-Origin: *,
Access-Control-Allow-Methods: GET, POST,
Access-Control-Allow-Headers: X-Requested-With

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.