2

I'm testing Angular application which uses Cookie-to-header token CSRF protection. According to Angular documentation https://angular.io/guide/http#security-xsrf-protection:

When performing HTTP requests, an interceptor reads a token from a cookie, by default XSRF-TOKEN, and sets it as an HTTP header, X-XSRF-TOKEN. Because only code that runs on your domain could read the cookie, the backend can be certain that the HTTP request came from your client application and not an attacker.

My application works like that:

  • header X-Xsrf-Token - correct value
  • cookie XSRF-TOKEN - correct value
  • Response: 200 OK

Example request:

enter image description here

  • header X-Xsrf-Token - random test value
  • cookie XSRF-TOKEN - correct value
  • Response: 403 Forbidden

Example request:

enter image description here

  • header X-Xsrf-Token - correct value
  • cookie XSRF-TOKEN - random test value
  • Response: 200 OK

Example request:

enter image description here

So it seems that the server verifies token correctness only for header X-Xsrf-Token. Generally, Cookie-to-header protection works by comparing cookie and header values, but I'm not sure if not comparing a cookie with a header, in this case, is a security problem. This protection assumes that only code that runs on my domain can read the cookie, so the token value must come from the cookie value if correct. Are there any security implications of not comparing cookie with a header in the case where the header value is being verified by the backend server?

Also form this documentation:

To take advantage of this, your server needs to set a token in a JavaScript readable session cookie called XSRF-TOKEN on either the page load or the first GET request. On subsequent requests the server can verify that the cookie matches the X-XSRF-TOKEN HTTP header, and therefore be sure that only code running on your domain could have sent the request.

But why the server should verify the header with cookie, not only header correctness?

2 Answers 2

1

For browsers specifically, unless your CORS configuration is totally broken (configured to send pre-flight responses that allow untrusted sites to set the X-Xsrf-Token header, and also return Access-Control-Allow-Credentials: true plus reflect the untrusted origin in the Access-Control-Allow-Origin header, which is a catastrophic security flaw already), you don't need the cookie or header value at all. The browser just won't let a malicious page send the request (with the custom header) in the first place, and so if a request arrives, you can merely check the existence of the header and ignore its value.

For that matter, you don't even need the custom header, so long as your server enforces one of the two things: that the content type is "application/json" (or anything else that can't be sent from an HTML form element... but be aware that it does need to parse the content type correctly!), or that the HTTP method (sometimes called "verb") is anything other than HEAD, GET, or POST. The browser will trigger a CORS pre-flight before sending a cross-origin PUT request, or any cross-origin request with Content-type: application/json, or any cross-origin request with any custom header at all. Unless the browser gets back a CORS preflight response that explicitly authorizes all three of the request origin, the use of credentials, and the specified behaviors (method, headers, etc.), the browser just won't send the actual attacker-specified request.

For things that aren't browsers, it usually doesn't even matter, because other clients don't typically automatically append cookies without requiring the caller to know what they are so CSRF isn't an issue (it relies on cookies going automatically). Non-browsers also don't typically accept instructions from arbitrary servers on the internet to send custom HTTPS requests to other servers, so the "cross-site" element is often missing too. The main exceptions are of course browser-like things, such as webviews in apps.


The whole point of the double-submit cookie pattern (the one being examined here) is that it's stateless; the server doesn't need to tie the user session to the anti-CSRF value. That's a nice property, but there are better ways to achieve it. If you don't need statelessness, though, the classic option is to have the (stateful) anti-CSRF token just embedded in the HTML form as a <input type="hidden" name="anti-csrf"> value, which is both generated and checked server-side and is immune to some of the risks of double-submit cookies. Making a double-submit cookie pattern use a stateful, verified value... that's pointless.

I notice you don't actually test what happens if you set both the cookie and the header to the same, invalid value. This represents the usual weakness of double-submit cookies: if an attacker can plant a cookie (of which there are several ways, though all can be prevented), then the attacker chooses an anti-CSRF value, plants it on the victim's browser in the cookie, and then has the victim's browser forge a request with the chosen value in the other location (which is usually the request body, but in this case is a custom header).

If you want a stateless anti-CSRF pattern, and don't want to rely on CORS protections, the best approach is to have the anti-CSRF token be derived from some user-specific secret (typically the session token), such as via a hash or even HMAC (with a server-specific key). That way, attackers can't learn the expected anti-CSRF token (because they don't know the session token or similar secret), and but the server doesn't have to remember anything (or at least not anything extra; in particular it doesn't need to remember each user's anti-CSRF token).

1

One assumption here that I think you didn't mention explicitly is that this requires the server to be stateful and keep a cache of active sessions. Comparing the cookie and header can be done completely statelessly (that difference is largely irrelevant for security, but has huge implications for performance and load-balancing).

The only attack I can think of with the way you describe it is that if there is an XSS vuln in the victim page that allows the attacker to exfiltrate the cookie value, then they could send valid requests from another tab in the same browser (assuming CORS does not block them) -- but of course if it's fully exfiltrated then they could do the attack from curl. I would probably call that a Medium finding, .. but would want to do a few more tests with dummy web apps to confirm.

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.