1

I'm currently struggling with the implementation of CSRF in a web application. The application is (more or less) stateless. There is however a session that is kept in a (HttpOnly) cookie. As we use this cookie we are subject to CSRF. But as the session is the only state the application has, I'm wondering how I could do the CSRF protection.

My current approach is to hash the session information and use this as CSRF token. Thus I can recreate the token for validation based on the session and (almost) no session information is available to the JS code. But I kind of dislike the idea that there is a connection from session to CSRF token as CSRF tokens should be random.

I found this post on doing CSRF in a stateless application that focuses on generating the CSRF token in the JS code and sending it as cookie and header. This approach seems to be very reasonable. But client-side generation of the CSRF token seems to be rather uncommon and most guides only mention server-side generated tokens.

Thus I'm wondering if there are any drawbacks with the approach of generating the CSRF token on the client-side?

8
  • 2
    This is the "double submit cookie" CSRF protection pattern. You can read about it here: cheatsheetseries.owasp.org/cheatsheets/… It probably has a few more weaknesses than your typical CSRF protection method, but your options are definitely more limited in a stateless environment. Aug 7, 2020 at 9:40
  • @ConorMancone would you consider this approach to be better than HMAC the session? I don't really get how I could use an encrypted cookie or HMAC with client generated tokens as the client would need the secret as well...
    – dpr
    Aug 7, 2020 at 9:49
  • HMAC the session? Can you clarify what process you have in mind? Aug 7, 2020 at 11:03
  • Generate a hash of the session's ID. As described here as well.
    – dpr
    Aug 7, 2020 at 11:06
  • Oh, okay. I follow. I'll see if I have time for a more detailed answer later. In general though hashing the session won't add anything over simply including the session cookie in the header/body (unless you are trying to minimize encoding issues). What you propose is still, fundamentally, the double submit cookie pattern Aug 7, 2020 at 11:08

2 Answers 2

2

The actual answer to your question depends entirely on how the token is generated and used. In some cases, doing it server-side is a necessity if you don't want to trade away other security. In other cases, it makes no difference.


There is only one important part of "generating the CSRF token in the JS code and sending it as cookie and header" and it's the "header" part. You can't send custom headers (or standard headers with any but a narrow set of values) in cross-site requests, unless CORS is specifically configured on the server to allow it. Thus, a completely fixed header name/value pair of IsCsrf: no is sufficient to prevent CSRF, so long as no third-party sites are allowed (per CORS preflight responses) to send requests with that header set. No randomness required.

Anyhow, that approach certainly doesn't require the server to pick a string, except in the sense of "the server should probably have a known header name it looks for". Nothing else matters, and it's fine for every user to use the same string (so long as there's no way for one user to force another to make a forged request from the target site directly... but of course that's not Cross-Site Request Forgery anymore).

Of course, that approach means all state-changing requests must be sent using script, which is undesirable for some sites (and already inherent for some others). Doing the more traditional version of double-submit cookie - where the value is both in a cookie and in the request body - is much more broadly compatible, but suffers from the thing where the security model of cookies is kind of a mess (what happens if the same cookie name is set with different values in Secure and non-Secure contexts? Why is it possible at all for a value set in a secure context to ever be visible in an insecure one, or vice versa?). There are various approaches to solving this - the most portable and widely-supported being HTTP Strict Transport Security (HSTS) as this effectively makes all cookies "Secure" whether they are actually flagged that way or not and totally obviates the issue of non-secure contexts - but if you're going for modern solutions you can also just use the SameSite flag and just break the assumption that cookie-based sessions mean CSRF risk.

If you go with traditional double-submit cookies, I don't see any reason they need to be generated server-side. The important part is that something a cross-site request controls (the anti-CSRF token in the body) and something the cross-site request (hopefully) doesn't control (the anti-CSRF token in the cookie) match, or at least have some tightly coupled relationship (it's fine for one to be a cryptographic hash digest of the other, for example). On the other hand, double-submit cookies are... not a great approach, because there are too many ways to plant a cookie on a victim that suddenly you need to be careful about.


Or you can go with your initial idea, of hashing the session token and using that value as the anti-CSRF token! This is actually a great idea, one I wish I saw more often. It's as stateless as your session token. It's as secure as your session token (which had better be "very"). It inherently solves a problem I've seen in home-rolled anti-CSRF code, where the CSRF tokens are generated and checked securely but nobody remembered to tie them to a session, so I can log in, see my CSRF token, and use it to CSRF you even though you're supposed to have a different one. The meaningful requirement of anti-CSRF tokens isn't actually that they be "random", it's that they be "unpredictable", which is less strict. Since the exact same constraint applies to session tokens (e.g. a JWT is not at all random, but it does contain a very unpredictable signature), it's perfectly reasonable to use the session token as a source for the anti-CSRF token. Leaving the session token as HttpOnly and hashing it to get the anti-CSRF token means no risk of somebody stealing the session token through XSS (though of course, remote-controlling the site using script injected via XSS will still work; stealing the session token itself is less relevant than some people think).

If you're still concerned about randomness, you can easily solve this by using a (random) salt and concatenating the (unhashed, plain-text) salt with the hash digest rather than using the digest alone... but I'd love to hear any argument for any attack this actually protects against.

Obviously, if you use the "hash of the session token" approach and your session token is still HttpOnly, you do need to generate the anti-CSRF token server-side. Fortunately, that's really simple to do. The server won't even need to re-compute it except when actually checking the value; the value could be computed once and stored client-side (in local / session storage), then get dynamically injected into any HTML form or similar request where the protection is needed.

4
  • In the double-submit cookie approach, are you aware of any advantages to them being cryptographically related rather than simply equal to one another? Mar 21, 2022 at 19:26
  • 1
    Cryptographically relating the cookie value and the request-body value (with no user-specific data) achieves little (maybe mitigates an impractical timing attack). An attacker with the ability to plant a cookie can obtain a valid cookie+body value pair by logging in with their own account, and then use the pair assigned to them to attack the victim via CSRF. However, cryptographically relating the anti-CSRF token (in the request body) to the session token is viable (e.g. by hashing the session token), and indeed avoids the need for a dedicated anti-CSRF cookie at all.
    – CBHacking
    Mar 23, 2022 at 13:19
  • Why not just use the session cookie value itself as the form token? Apr 4, 2022 at 18:51
  • 1
    @DrazenBjelovuk You can, but doing so eliminates the main advantage cookies have over JS local storage as a place to put your session/access token: the ability to use httponly so it can't be stolen by JS (in an XSS scenario). If you go putting it in the form body, a malicious script can read it straight out of the DOM. Still, you're correct that this approach protects against CSRF... and the XSS is very bad even if stealing the token isn't available so the marginal security benefit isn't huge.
    – CBHacking
    Apr 5, 2022 at 7:10
0

I read the same article and i think it's wrong. First of all, as per OWASP says "CSRF tokens should be generated on the server-side." Also client side means everything is public, compare to server-side which you can add secret to make it more secure, client side expose the token generation process. which i think is really bad idea. I am surprised this article ranked so high on search results.

Example

Target Url : http://bank.com/transfer-money/1000
CSRF cookie : CSRF:(value generated by front-end and attacker knows it too)
Header: X-CSRF : (attacker generate using same algorithm)

I can send this request to 10000 users and it could pass the CSRF check technically (but I admit it is rare to happen)
Also from security point of view, it's good practice to let user know as less as possible of your system

3
  • I don't see how exposure of the generation process is relevant as long as the cookie is secure and unpredictable. Mar 21, 2022 at 19:43
  • If I know how your CSRF cookie is generated, I can fake one and send it in header in a CSRF attack, I would say it's hard to perform such attack but it is possible
    – ExploreEv
    Mar 22, 2022 at 20:15
  • But you would need to match it in the cookie. Mar 31, 2022 at 1:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .