47

We've recently were handed a security report containing the:

Cookie(s) without HttpOnly flag set

vulnerability, which we apparently had in one of our internal applications.

The applied fix was as simple as setting the Django's CSRF_COOKIE_HTTPONLY configuration parameter to True.

But, this is what got me confused. Django documentation says:

Designating the CSRF cookie as HttpOnly doesn’t offer any practical protection because CSRF is only to protect against cross-domain attacks. If an attacker can read the cookie via JavaScript, they’re already on the same domain as far as the browser knows, so they can do anything they like anyway. (XSS is a much bigger hole than CSRF.)

Although the setting offers little practical benefit, it’s sometimes required by security auditors.

Does this mean that there is no actual vulnerability here and we just have to be complied with the security auditing rules?

56
+50

As joe says, there is no real security benefit to this. It is pure security theater. I'd like to highlight this from the documentation:

If you enable this and need to send the value of the CSRF token with an AJAX request, your JavaScript must pull the value from a hidden CSRF token form input on the page instead of from the cookie.

The purpose of the HttpOnly flag is to make the value of the cookie unavailable from JavaScript, so that it can not be stolen if there is a XSS vulnerability. But the CSRF-token must somehow be available so it can be double submitted - thats the whole point with it, after all. So Django solves this by including the value in a hidden form field. This negates the whole benefit of HttpOnly, since an attacker can just read the value of the form field instead of the cookie.

  • 12
    To rephrase and summarise your first two sentence slightly, for emphasis: the purpose of the HttpOnly flag is to make the value of the cookie unavailable from your site's JavaScript, but in an Ajax-powered webapp the entire function of a CSRF token relies upon it being available from your site's JavaScript. That's what makes the security report here so wrongheaded. – Mark Amery Dec 15 '17 at 10:29
  • 16
    Additionally, a little experimentation suggests to me that including the CSRF token in a form field is meaningfully less secure than including it in a non-HTTP-only cookie, since if you ever (perhaps accidentally) include an Access-Control-Allow-Origin: * header with a response that includes the CSRF token, it will be readable by attackers if it's included in the response body - but not if it's included in a Set-Cookie header, since reading those in JavaScript is impossible. – Mark Amery Dec 15 '17 at 10:41
  • @MarkAmery Am I missing something? Can't javascript read any cookies which aren't configured with httpOnly? The browser takes the Set-Cookie header and adds it to your cookies as soon as the request completes, and then any JS in the browser can read it (unless httpOnly is set). So it might be true that JS can't read the header itself, but once the header is added to your cookies, it can be read by JS. – colllin Oct 1 at 21:47
  • 1
    @colllin JavaScript can read any cookies associated with the domain of the page that the JavaScript is executing on. But in the context of discussing a CSRF attack, we're often imagining a scenario involving an attacker who lures you to a malicious page on a domain the attacker controls which has JavaScript scripts that cause you to submit requests to the site being attacked. The scripts on that malicious page can't read cookies from the victim site, regardless of CORS headers, but may be able to read response bodies if the innocent site's CORS headers are permissive. – Mark Amery Oct 1 at 22:59
  • 1
    @MarkAmery If using ACAO: * would expose the CSRF token, then you have a much bigger problem, because you're sending an authenticated user's CSRF token in response to unauthenticated requests! ACAO: * cannot be combined with Access-Control-Allow-Credentials: true - it is a violation of the spec to do so, and the browser will treat the request as though it had no ACAO at all - but the browser won't send auth cookies if you don't specify withCredentials and if you do specify withCredentials but ACAC is missing or false, it's the same as if ACAO is missing or for another domain. – CBHacking yesterday
9

That is correct. This is a false positive and the person providing this finding to you does not understand what they are doing unfortunately. Someone that understood the risks of mitm and csrf attacks would never provide this to you.

  • 12
    While this argument is true in this particular case it is recommended to employ strict security settings by default (which includes using httponly) and only have less strict settings if this is really needed by the application and only if one is really sure that loosening security setting does not cause harm in the specific case. In other words: unless there is a reason the CSRF cookie can not be httponly it should be httponly too. Thus, I would actually agree with the security report because not having secure defaults whenever possible is a bad idea. – Steffen Ullrich Dec 15 '17 at 5:05
  • 2
    @SteffenUllrich How could a double submit token work if it is not available from JS? The Django option in question does more than just setting a flag. See my answer. – Anders Dec 15 '17 at 5:22
  • 3
    @Anders: the statement you quote in your nice answer shows a different picture than the question and this answer. And, it does not contradict my comment which said "... unless there is a reason the CSRF cookie can not be httponly it should be httponly too". Obviously in this case there is a reason it should not be httponly and you've showed it in your answer. – Steffen Ullrich Dec 15 '17 at 5:36
  • 3
    @SteffenUllrich While I agree with your sentiment in general, encouraging people to reason about complex security themselves ("unless there is a reason the CSRF cookie can not be httponly it should be httponly too") may be worse than clearly stating "Yeah, CSRF cookies are an exception, you can make them httponly because what they are doing does not need httponly to be secure.". It encourages people to apply reasoning to other areas where the case isn’t as clear and where they might end up with the wrong (insecure) result. – Jonas Schäfer Dec 15 '17 at 8:34
  • @joe yes they would as you pay for a full report and generally that flag is security issue. Unless you say that this is 'out of scope' I would expect that to come back in a report. Don't expect pentesters to understand 100% of the environment you work in or frameworks you choose - its a vulnerability, its up to YOU to figure out the business risk – McMatty Nov 29 '18 at 23:51
2

Designating the CSRF cookie as HttpOnly doesn’t offer any practical protection because CSRF is only to protect against cross-domain attacks.

This can be stipulated in a much more general way, and in a simpler way by remove the technical aspect of "CSRF cookie".

Designating a cookie as HttpOnly, by definition, only protects against access via document.cookie or equivalent JS methods. It doesn't prevent any HTTP interaction that may have been caused by JS code; any interaction that the user does via HTML elements, like a form submission, can be started by JS. There is no meaningful distinction of how something was started; in fact, you can claim "the user started it" by pointing to the user typing it the URL of the current Webpage, or that of some Webpage that linked to the current Webpage.

That is also the reason why the concept of "autoplay of video" isn't well defined, and cannot be prevented reliably: what constitutes a voluntary user action to start a video is a user interface concept, not a DOM (Document Object Model) concept. The browser doesn't know autoplay from user made a gesture to play a video, unless the video starts before the user makes any move (such as "space to scroll down"). (One can try to "wack a mole" autoplay in a few cases, like one can try to "wack a mole" (detect, black-list) annoying Web ads, or sharing information with third party domains, but without guarantee of coverage.)

Unless JS is completely turned off on a domain, any user action inside the frame controlled by the Website should simply be assumed to be doable by script of the Website. One the modern Web, who turns off JS completely on most domains? Almost no one.

So the generalized observation is:

Designating any cookie as HttpOnly doesn’t offer any practical protection against any against attacks that perform actions that the user might perform through the interface of the Website.

Note that reading all cookies (including those marked HttpOnly) is not done by the interface of the website, it can be done only with the Inspector tool of the browser, or the HTTP proxy for cookies sent over HTTP.

1

While the CSRF-Token is no secret on a page with a CSRF protected form (and could be stolen using XSS), you may have a XSS vulnerability in some page without a form. There you can only get the token from the cookie but not from a hidden field. In this case the security is improved by using a HttpOnly cookie.

There is still a way to get the token by requesting a page which contains a form via javascript. There may be ways to prevent this, but on most sites this attack will be possible.

  • 2
    If there is a XSS vulnerability on a paget without a form I can just request a page with a form and read the token (a little extra work, but not a lot). And if there is an XSS vulnerability, why would I need CSRF anyway? I can just do what I want. – Anders Dec 22 '17 at 14:23
  • That's a point. To your question: CSRF protection is a way to make XSS less dangerous. If you inject some script which tries to submit a POST request, it needs to get the CSRF token from somewhere. As you said, it is quite possible to try to extract it by loading another page which contains a form. – allo Dec 22 '17 at 14:27
  • @allo No, CSRF protection does not make XSS even slightly less dangerous and it is quite irresponsible to claim so! The necessity of using XSS-injected script to either make a same-origin GET request to any page with a CSRF form token or just set the cookie yourself using JS (assuming it's not authenticated to the session in any way, which it usually isn't) is nothing but an utterly trivial speedbump. Pretending this will make you any secure is simply wishful thinking, and any competent attacker would route around this "protection" without even noticing it was there. – CBHacking yesterday
  • I addressed the question, what advantages HttpOnly has, not if it eliminates XSS vulnerabilities. In the comment above yours I clarified and described what can still be used to circumvent the additional protection, that HttpOnly provides. I see your point, but my post and comment already contain the limits of the security that this setting is able to provide. I think you should add your points as an additional answer to the question, because you're more addressing the question than my answer and you're points are enough to write an answer. – allo yesterday
1

I think the main point of confusion here is that the Django docs are specifically talking about the CSRF use case for a cookie. In order to understand why the httpOnly flag adds no value in preventing CSRF, you need to understand both CSRF and how cookies work.

CSRF is when a 3rd party triggers your user's browser to make a request to your server, and their browser automatically sends your server's cookies along with the request, as expected. What you don't want is for your server to interpret this request as actually coming from your user, so you use a CSRF mitigation technique. The whole point of CSRF mitigation is to be able to detect when the request didn't come from your own domain (i.e. from your user interacting with your application).

Briefly, how cookies work: Whenever a user's browser sends a request to your domain/server, it automatically sends all the cookies associated with your domain, regardless of the httpOnly flag. Cookies therefore allow your client or your server to attach information to a user's browser that will be returned to your server automatically along with any follow-on requests. Cookies with the httpOnly flag cannot be accessed from javascript. They probably shouldn't be considered a secure place to store information, but they do provide the advertised functionality.

Back to CSRF implemented using a cookie — in this case the httpOnly flag is pointless — the crux of CSRF is that they don't need to read your user's cookies, they just need your user's browser to send the associated cookies to your server along with the network request they forced it to send. The httpOnly flag, in general, does provide value in that it prevents client access to those cookies, and if your server returns any cookies, you should probably make them httpOnly. If you are using a cookie for CSRF, then, you shouldn't do that, and you should spend your time rethinking that rather than making it an httpOnly cookie. So, in general, it seems like that is a good rule of thumb — your server shouldn't send any non-httpOnly cookies unless it is specifically intended to be accessed by the client javascript.

0

The only way CSRF prevention with double-submitting can work is by sending the nonce in a cookie. If you send it in the HTTP response body, it can in some cases be parsed out by a script sending a cross-domain request, (if you've allowed CORS for that page) which defeats the whole purpose of protecting against CSRF. The idea is that scripts on domain X can’t get the value of cookies on domain Y, this is one f the main pillars of security on the Web.

With HTTPOnly cookies you won’t be able to protect against CSRF using the most common method - namely double-submitting a nonce via cookies and a script on the page able to read the cookies. Here is how that technique works:

1) Generate a nonce value that you store on the server for the session. It could be the session ID itself, or something stored in the data associated to the session.

2) Send this to the client via the cookie headers without HTTPOnly, have some Javascript grab it and store it (e.g. short term or in sessionStorage)

3) Submit this with every request that you want to protect from CSRF. The request would have the sessionId in the cookie, and also this nonce in the request body or into the URL querystring (e.g. for GET requests). If you are worried about the nonce winding up in logs or whatever, you can instead use Web Crypto to sign request payloads with HMAC using this nonce

4) The server will look up the session data, and check if the nonce doesn't match, throws an error and generates another nonce and sends in the cookie. Make sure to send the nonces only in cookies!

It is unfortunate but true that many “security audit” tools start to flag cookies not marked with httpOnly and recommend that this flag be added. This flag is sadly worse than useless: it is security theater.

I'm talking only about the httpOnly flag. It's worse than useless because it makes you think that you dodged some kind of bullet, when in fact that same class of attack can still happen: the attacker needs to craft a script to send the cookie to their server, so they might as well have that script execute the actual requests in the context of your authorized session, the same way that they'd do it if they had your cookie. They would write the script ahead of time. No real attacker would sit there at 3 AM making requests because their victim finally activated a script that sent them the cookie. Far more likely, that script is already pre-programmed to do what it needs to do. And if httpOnly cookies didn't exist, the people would care more about sanitizing their Javascript output to prevent XSS, which is the only correct way to prevent this attack.

  • 1
    "If you send it in the HTTP response body, it can be parsed out by a script sending a cross-domain request..." This is FALSE unless you have severely screwed up your CORS configuration, a very serious vuln in its own right. SOP prevents cross-origin requests from being read by the originating script. This approach provides some defense-in-depth against such a catastrophic misconfiguration of CORS, but the marginal security gain from such DiD is much less than you'd get by using something more secure than double-submit cookies for CSRF protection, which is relatively easy to bypass. – CBHacking yesterday
  • "With HTTPOnly cookies you won’t be able to protect against CSRF using the most common method - namely double-submitting a nonce via cookies and a script on the page able to read the cookies." Can you provide more background on this technique? "...to prevent XSS, which is the only correct way to prevent this attack." I believe XSS concerns are orthogonal to CSRF concerns. – colllin yesterday
  • @CBHacking thank you, I have updated my answer to make it clearer that the vulnerability should only appear if you mishandle CORS or fail to use HTTPS. – Gregory Magarshak yesterday
  • @collin does that help? – Gregory Magarshak yesterday
  • 1
    @GregoryMagarshak HMAC of the session token (which is already a de-facto nonce) or some other unpredictable value tied to the user (and ideally the session). This prevents cookie planting attacks, header-injection (for adding a cookie) attacks, session fixation-type attacks, and at least partially avoids the risk of using a bad PRNG for the token (although then there's the question of how secure your session tokens are). It doesn't require extra server-side state, can be done either with or without using cookies, and doesn't expose the session token even if the CSRF token is exposed. – CBHacking yesterday

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.