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 might think it should be, but really it's not at all!
According to the ancient Netscape cookie_spec the entire
NAME=VALUE
string is:So
-
should work, and it does seem to be OK in browsers I've got here; where are you having trouble with it?By implication of the above:
=
is legal to include, but potentially ambiguous. Browsers always split the name and value on the first=
symbol in the string, so in practice you can put an=
symbol in the VALUE but not the NAME.What isn't mentioned, because Netscape were terrible at writing specs, but seems to be consistently supported by browsers:
either the NAME or the VALUE may be empty strings
if there is no
=
symbol in the string at all, browsers treat it as the cookie with the empty-string name, ieSet-Cookie: foo
is the same asSet-Cookie: =foo
.when browsers output a cookie with an empty name, they omit the equals sign. So
Set-Cookie: =bar
begetsCookie: bar
.commas and spaces in names and values do actually seem to work, though spaces around the equals sign are trimmed
control characters (
\x00
to\x1F
plus\x7F
) aren't allowedWhat isn't mentioned and browsers are totally inconsistent about, is non-ASCII (Unicode) characters:
so in practice you cannot use non-ASCII characters in cookies at all. If you want to use Unicode, control codes or other arbitrary byte sequences, the cookie_spec demands you use an ad-hoc encoding scheme of your own choosing and suggest URL-encoding (as produced by JavaScript's
encodeURIComponent
) as a reasonable choice.In terms of actual standards, there have been a few attempts to codify cookie behaviour but none thus far actually reflect the real world.
RFC 2109 was an attempt to codify and fix the original Netscape cookie_spec. In this standard many more special characters are disallowed, as it uses RFC 2616 tokens (a
-
is still allowed there), and only the value may be specified in a quoted-string with other characters. No browser ever implemented the limitations, the special handling of quoted strings and escaping, or the new features in this spec.RFC 2965 was another go at it, tidying up 2109 and adding more features under a ‘version 2 cookies’ scheme. Nobody ever implemented any of that either. This spec has the same token-and-quoted-string limitations as the earlier version and it's just as much a load of nonsense.
RFC 6265 is an HTML5-era attempt to clear up the historical mess. It still doesn't match reality exactly but it's much better then the earlier attempts—it is at least a proper subset of what browsers support, not introducing any syntax that is supposed to work but doesn't (like the previous quoted-string).
In 6265 the cookie name is still specified as an RFC 2616
token
, which means you can pick from the alphanums plus:In the cookie value it formally bans the (filtered by browsers) control characters and (inconsistently-implemented) non-ASCII characters. It retains cookie_spec's prohibition on space, comma and semicolon, plus for compatibility with any poor idiots who actually implemented the earlier RFCs it also banned backslash and quotes, other than quotes wrapping the whole value (but in that case the quotes are still considered part of the value, not an encoding scheme). So that leaves you with the alphanums plus:
In the real world we are still using the original-and-worst Netscape cookie_spec, so code that consumes cookies should be prepared to encounter pretty much anything, but for code that produces cookies it is advisable to stick with the subset in RFC 6265.