Yes, HTTP-Only cookies would be fine for this functionality. They will still be provided with the XmlHttpRequest's request to the server.
In the case of Stack Overflow, the cookies are automatically provided as part of the XmlHttpRequest request. I don't know the implementation details of the Stack Overflow authentication provider, but that cookie data is probably automatically used to verify your identity at a lower level than the "vote" controller method.
More generally, cookies are not required for AJAX. XmlHttpRequest support (or even iframe remoting, on older browsers) is all that is technically required.
However, if you want to provide security for AJAX enabled functionality, then the same rules apply as with traditional sites. You need some method for identifying the user behind each request, and cookies are almost always the means to that end.
In your example, I cannot write to your document.cookie, but I can still steal your cookie and post it to my domain using the XMLHttpRequest object.
XmlHttpRequest won't make cross-domain requests (for exactly the sorts of reasons you're touching on).
You could normally inject script to send the cookie to your domain using iframe remoting or JSONP, but then HTTP-Only protects the cookie again since it's inaccessible.
Unless you had compromised StackOverflow.com on the server side, you wouldn't be able to steal my cookie.
Edit 2: Question 2. If the purpose of Http-Only is to prevent JavaScript access to cookies, and you can still retrieve the cookies via JavaScript through the XmlHttpRequest Object, what is the point of Http-Only?
Consider this scenario:
- I find an avenue to inject JavaScript code into the page.
- Jeff loads the page and my malicious JavaScript modifies his cookie to match mine.
- Jeff submits a stellar answer to your question.
- Because he submits it with my cookie data instead of his, the answer will become mine.
- You vote up "my" stellar answer.
- My real account gets the point.
With HTTP-Only cookies, the second step would be impossible, thereby defeating my XSS attempt.
Edit 4: Sorry, I meant that you could send the XMLHttpRequest to the StackOverflow domain, and then save the result of getAllResponseHeaders() to a string, regex out the cookie, and then post that to an external domain. It appears that Wikipedia and ha.ckers concur with me on this one, but I would love be re-educated...
That's correct. You can still session hijack that way. It does significantly thin the herd of people who can successfully execute even that XSS hack against you though.
However, if you go back to my example scenario, you can see where HTTP-Only does successfully cut off the XSS attacks which rely on modifying the client's cookies (not uncommon).
It boils down to the fact that a) no single improvement will solve all vulnerabilities and b) no system will ever be completely secure. HTTP-Only is a useful tool in shoring up against XSS.
Similarly, even though the cross domain restriction on XmlHttpRequest isn't 100% successful in preventing all XSS exploits, you'd still never dream of removing the restriction.
Although there is the RFC 2965 (Set-Cookie2
, had already obsoleted RFC 2109) that should define the cookie nowadays, most browsers don’t fully support that but just comply to the original specification by Netscape.
There is a distinction between the Domain attribute value and the effective domain: the former is taken from the Set-Cookie
header field and the latter is the interpretation of that attribute value. According to the RFC 2965, the following should apply:
- If the Set-Cookie header field does not have a Domain attribute, the effective domain is the domain of the request.
- If there is a Domain attribute present, its value will be used as effective domain (if the value does not start with a
.
it will be added by the client).
Having the effective domain it must also domain-match the current requested domain for being set; otherwise the cookie will be revised. The same rule applies for choosing the cookies to be sent in a request.
Mapping this knowledge onto your questions, the following should apply:
- Cookie with
Domain=.example.com
will be available for www.example.com
- Cookie with
Domain=.example.com
will be available for example.com
- Cookie with
Domain=example.com
will be converted to .example.com
and thus will also be available for www.example.com
- Cookie with
Domain=example.com
will not be available for anotherexample.com
- www.example.com will be able to set cookie for example.com
- www.example.com will not be able to set cookie for www2.example.com
- www.example.com will not be able to set cookie for .com
And to set and read a cookie for/by www.example.com and example.com, set it for .www.example.com
and .example.com
respectively. But the first (.www.example.com
) will only be accessible for other domains below that domain (e.g. foo.www.example.com or bar.www.example.com) where .example.com
can also be accessed by any other domain below example.com (e.g. foo.example.com or bar.example.com).
Best Answer
You were on the right track with this:
The CSRF cookie isn't meant to be "sent" to the server, it is meant to be read by the client and then supplied in a custom HTTP request header. Forged GET requests (triggered by HTML tags such as
<img src="">
) from other domains cannot set custom headers, so this is how you assert that the request is coming from a javascript client on your domain.Here is how you can implement the idea you were working on, imagine you have
api.domain.com
andui.domain.com
:1) User loads the Angular client from
ui.domain.com
2) User posts authentication information from Angular client to
api.domain.com
2) Sever replies with an
HttpOnly
authentication cookie, calledauthCookie
, and a custom header e.g.X-Auth-Cookie
, where the value of this header is a unique value that is linked to the session that is identified by theauthCookie
3) The Angular client reads the
X-Auth-Cookie
header value and stores that value in aXSRF-TOKEN
cookie on its domain,ui.domain.com
So now you have:
XSRF-TOKEN
cookie onui.domain.com
authCookie
cookie onapi.domain.com
4) User makes a request of a protected resource on
api.domain.com
. The browser will automatically supply theauthCookie
value, and Angular will automatically send theX-XSRF-TOKEN
header, and will send the value that it reads from theXSRF-TOKEN
cookie5) Your server asserts that the value of
X-XSRF-TOKEN
is linked to the same session that is identified by the value of theauthCookie
I hope this helps! I've also written about token authentication for Angular, Token Based Authentication for Single Page Apps (SPAs) (Disclaimer: I work at at Stormpath)