11

It seems that the entire problem could be solved very elegantly by simply adding a new flag to the HTTP cookie specification.

Similarly to how cookies flagged Secure will only be submitted by the user agent over secure connections and those which are flagged HttpOnly will be forever inaccessible via DOM access, why not specify a new flag, say, NoCSR, or SameOriginOnly, which, when set, would prevent the cookie from being submitted with requests triggered by cross-origin referrers?

Of course, it would default to off so as not to break the previously expected behavior for already existing web sites, as per HTML5 Design Principle #2.1. I suppose there does exist one security hole, but not one that can't be easily solved: it can't be indiscriminately depended on, because old browsers would presumably just ignore the unrecognized flag.

So why not just create a whitelisted enumeration of NoCSR-implementing user agents versions' corresponding UA strings, and then only accept/process authorization-requiring and sensitive requests that carry one of those User-Agent: values? For requests submitted with no or non-whitelisted UA headers, an error could be returned that reasonably demands that the user upgrade to a supported browser version.

The whitelisting could be DRYed out with canonical implementation libraries (it would probably be more like a single simple function) for various server side languages.

Doesn't this seem much simpler than the current system of generating, keeping track of, and reading secure CSRF tokens?

Thoughts, anyone?

wwaawaw
  • 521
  • 4
  • 9
  • 1
    ``For requests submitted with no or non-whitelisted UA headers, an error could be returned that reasonably demands that the user upgrade to a supported browser version.`` which would definetly ``break the previously expected behavior for already existing web sites``. – Jonas Schäfer Sep 09 '12 at 14:31
  • 1
    Cookies are not the only means of authentication. You could also use HTTP authentication, client certificate based authentication, or even IP address based authentication. – Gumbo Sep 09 '12 at 14:32
  • Dom't use anything as unpredictably volatile as IP addresses for authentication. I don't know what client certificate based authentication refers to, but is HTTP authentication vulnerable to CSRF attacks, as well? I suppose it would be, wouldn't it.. That said, I feel like my solution would still work for the overwhelming majority of sites using cookie-based auth schemes, wouldn't it? @Gumbo –  Sep 09 '12 at 14:38
  • 1
    @adlwalrus, yes, HTTP authentication and client certificate based authentication are also vulnerable to CSRF. – D.W. Sep 10 '12 at 00:41
  • CSRF tokens are an interesting matter and it looks like you grasp the reason for their existence. Your question however is why is this not implemented as a cookie - for which you have reasonable answers (and informed views about as well), which consequently makes your question title misleading. – Pedro Apr 24 '20 at 09:53

4 Answers4

7

Since this question was asked, just such a cookie flag has been added to modern browsers: SameSite. From the spec:

5.3.7. The SameSite Attribute

If the attribute-name case-insensitively matches the string
"SameSite", the user agent MUST process the cookie-av as follows:

  1. If cookie-av's attribute-value is not a case-insensitive match for "Strict" or "Lax", ignore the "cookie-av".

  2. Let "enforcement" be "Lax" if cookie-av's attribute-value is a case-insensitive match for "Lax", and "Strict" otherwise.

  3. Append an attribute to the cookie-attribute-list with an attribute-name of "SameSite" and an attribute-value of "enforcement".

5.3.7.1. "Strict" and "Lax" enforcement

Same-site cookies in "Strict" enforcement mode will not be sent along with top-level navigations which are triggered from a cross-site document context. As discussed in Section 8.8.2, this might or might not be compatible with existing session management systems. In the interests of providing a drop-in mechanism that mitigates the risk of CSRF attacks, developers may set the "SameSite" attribute in a "Lax" enforcement mode that carves out an exception which sends same-site cookies along with cross-site requests if and only if they are top- level navigations which use a "safe" (in the [RFC7231] sense) HTTP method.

Lax enforcement provides reasonable defense in depth against CSRF
attacks that rely on unsafe HTTP methods (like "POST"), but does not
offer a robust defense against CSRF as a general category of attack:

  1. Attackers can still pop up new windows or trigger top-level navigations in order to create a "same-site" request (as described in section 2.1), which is only a speedbump along the road to exploitation.

  2. Features like "<link rel='prerender'>" [prerendering] can be exploited to create "same-site" requests without the risk of user detection.

When possible, developers should use a session management mechanism such as that described in Section 8.8.2 to mitigate the risk of CSRF
more completely.

It's supported as of IE 11 (on Windows 10 only), Edge 16, Firefox 60, Chrome 51, Safari 12, and Opera 39, according to caniuse.com.

  • Even if that spec itself says `SameSite` won't fully prevent CSRF, this cookie attribute should be more widely known. – Xenos Aug 25 '18 at 18:57
7

You are right, your solution (a cookie that only works on the same origin) would prevent anti-CSRF tokens from being necessary against CSRF attacks. As for why nobody implemented this, we can only guess. Perhaps it is because a solution is already present in current header values, namely by checking the Referer header. Using the referrer is also more flexible than a NoCSR flag: you can whitelist multiple domains or subdomains.

Note that your proposal would not magically solve everything: cookies are also used when someone clicks a link and opens a page where they are logged in. Let's say you search for 'stackoverflow' and you click the top result, then you would like to be logged in even though the request came from another origin (namely your search engine). Therefore, you would need two cookies: one that has the proposed flag and one without. Or, alternatively, the flag would not apply to GET requests so that normal links work, but then you leave a lot of website still vulnerable when they modify the server state in response to GET requests (many applications will perform actions when you do requests like GET /deleteUser/123).

It's almost as if the browser should just include a flag about whether a request was across origins and then the web server can decide whether this is a dangerous action that should not be done across origins... which sounds a lot like the Referer header!

Luc
  • 31,973
  • 8
  • 71
  • 135
  • I don't believe that the HTTP spec requires referrer transmission. But if one assumes that their users have internal referrer transmission enabled, I assume just verifying that would do the trick too, wouldn't it? – wwaawaw Sep 10 '12 at 08:06
  • But what I don't understand is how a replay attack is an attack at all. Seems more like an innocent accident or a bug or a mishap. Also, isn't the risk of that mitigated by disabling / "greying out" the button on it's `click` event? I read a post about that one time, I think it was on codinghorror. I'd also redirect after the POST receiver script got hit. Finally, how would you verify the nonce when you received it back? – wwaawaw Sep 10 '12 at 08:09
  • @adlwalrus _"I don't believe that the HTTP spec requires referrer transmission."_ No, it doesn't, that's my single issue with this. All browsers support it though, and those who disabled it because of privacy reasons will discover soon enough. Same with Javascript, if it's disabled stuff might break. Just make sure to give appropriate errors. – Luc Sep 10 '12 at 09:41
  • Well that makes sense then. There really isn't any additional privacy leak to sending same-site referrers, though, and that's really all that's called for here. I believe the browsers that offer a way to disable referer transmission only allow disabling it for external link clicks. Of course there're extensions to both big browsers that disable it completely. But yeah, obviously there'd be a reasonably explanatory error message. – wwaawaw Sep 10 '12 at 10:29
  • @adlwalrus _"But what I don't understand is how a replay attack is an attack at all."_ Besides what Wikipedia has to say on this, a replay attack could be used to redo the action that has just been done, like receiving money/credits from another account twice, or ordering something twice, etc. _"how would you verify the nonce when you received it back?"_ 1) make sure it has been issued, and 2) that it hasn't been used in the past. Also make sure the entropy of the nonce is large enough (same as with session tokens), or an attacker might be able to guess it. – Luc Sep 10 '12 at 11:26
  • I think it's really fantastic how accessible information about security is becoming. Thanks! – wwaawaw Sep 10 '12 at 12:58
  • @Luc CSRF is not a replay attack. It is an impersonation attack where an attacking site is able to trigger requests in behalf of a victim. Although a CSRF token can also be used to mitigate replay attacks, it does not necessarily have to. A per-session token (or even per-user token) suffices to prevent CSRF as long as it is only accessible to the properly authenticated user. – Gumbo Sep 10 '12 at 17:05
  • @Gumbo I know CSRF is no replay attack, but using a nonce prevents both at once. – Luc Sep 10 '12 at 17:31
  • I read "As for why nobody implemented this, we can only guess. Perhaps it is because a solution is already present in current header values" in your answer. AFAIK SameSite is widely used today – guettli Apr 24 '20 at 07:10
4

I'd say mostly complication. A link sent through another application would carry no site referrer, so that is a hole that needs to be considered. One could focus on only passing requests from within the site, but there could be an XSS vulnerability that would create CSRF-like behavior that would be stopped by using a nonce.

In very short summary: the nonce method already works and isn't very complicated to implement. This proposed method may end up being much more complicated to implement, would break backwards compatibility, and would require the already existing nonce method as a stop-gap.

Finally, there are other non-repetition behaviors for which you might still desire a nonce.

Jeff Ferland
  • 38,090
  • 9
  • 93
  • 171
  • I don't think it actually would break BC, if you read the post carefully. Is "nonce" synonymous with "CSRF token"? – wwaawaw Sep 10 '12 at 08:10
  • 1
    @adlwalrus Essentially yes. – Jeff Ferland Sep 10 '12 at 15:42
  • 1
    @adlwalrus A CSRF token does not necessarily need to be used only once. The only requirement it must fulfill is that it is only known to the properly authenticated user. Theoretically, it is perfectly fine to generate a token only on user registration. Although regenerating the CSRF token reduces the risk of the possibility of a valid CSRF token getting leaked or guessed. But nonce rather implies that the value is only valid for a single request. – Gumbo Sep 10 '12 at 17:12
  • @Gumbo that's a great point. – wwaawaw Sep 10 '12 at 17:20
3

Doesn't this seem much simpler than the current system of generating, keeping track of, and reading secure CSRF tokens?

No, because this measure would only protect against state changing endpoints (e.g. HTTP POST of form data), but the other flavor of CCRF is just having a user click on a maliciously formed link ( e.g. check out this facebook <a href="yourbank.com/?s=somethingEvil">post</a> -> "check out this facebook post" ) without inspecting the url.

I understand that GET requests are supposed to be considered "safe" (see https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html), but if you were to have the browser implement a standard to prevent CSRF, then it would have to protect all cases. Event when the server implements an unsafe GET request.

Preventing CSRF requires multiple steps, including CSRF Tokens and a detailed explanation of prevention can be found in this OWASP Cheat Sheet. In summary it requires:

  • Checking if your framework has built in CSRF protections and use it. If not add CSRF tokens to all state changing requests
  • Always use SameSite Cookie Attribute for session cookies
  • Implement at least one of the following: use custom request headers, verify the origin with standard headers, use double submit cookies
  • Consider implementing user interaction based protection for highly sensitive operations (OTP, Re-authenticating, etc.)
  • Remember that any Cross-Site Scripting (XSS) can be used to defeat all CSRF mitigation techniques
  • Do not use GET requests for state changing operations
iraleigh
  • 326
  • 2
  • 11
  • They system I use (Django) does only add the CSRF-token to POST. See https://docs.djangoproject.com/en/3.0/ref/csrf/ and cookies have SameSite=Lax by django default. – guettli Apr 21 '20 at 18:22
  • 1
    @guettli I work at a large company in a regulated industry, so we use csrf tokens for all requests behind the login, regardless of GET/POST. This is because we have a very low risk tolerance and it can be hard to govern a lot of people. If you don't use CSRF tokens for GET requests, then make sure that your GET requests are safe (according to how "safe" is defined here https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) – iraleigh Apr 21 '20 at 19:33
  • @guettli I added some more info to my answer, hope it helps – iraleigh Apr 24 '20 at 00:58