21

I'm in need of a CSRF token, for a certain application that submits a form with POST. Ideally, I'd like to not make a DB call for each submission, to avoid storage and DB traffic & latency. To this end OWASP's "CSRF Prevention Cheat Sheet" says,

Overview

The Encrypted Token Pattern leverages an encryption, rather than comparison, method of Token-validation. After successful authentication, the server generates a unique Token comprised of the user's ID, a timestamp value and a nonce, using a unique key available only on the server. This Token is returned to the client and embedded in a hidden field. […] Non-AJAX form-based requests will implicitly persist the Token in its hidden field. On receipt of this request, the server reads and decrypts the Token value with the same key used to create the Token. […]

Validation

On successful Token-decryption, the server has access to parsed values, ideally in the form of claims.

The first line of "Validation" reads, to me, like the very defintion of a JWT. I'm not seeing why the information in token needs to be encrypted, either: it would seem it merely needs to be authenticated that it came from the server (as opposed to an attacker attempting forging a CSRF token.)

Is there any particular reason to encrypt CSRF tokens? If not, does a JWT suffice?

Also, the above token pattern includes a nonce. What is the point of the nonce? (The whole point of this pattern is to not store any state server-side. If I'm not storing state, how can I use the nonce during validation in any material manner?)

My main reason for pursuing JWTs here is that there's decent library support, I'll probably want them for auth sooner or later, and it mostly keeps me away from implementing crypto stuff.

My understanding of JWTs is that if I had JWTs for full user authentication, I could store those in localStorage, and simply add those to a field with JS prior to form submission. Unfortunately, the app uses cookie-based auth, and I'm not able to change that aspect right now.

Thanatos
  • 1,016
  • 2
  • 10
  • 16
  • It's helpful to define your acronyms. I think CSRF is readily known in the security community, but JWT looks relatively new. Keep in mind this is a cross-discipline field. – Steve Sether Feb 25 '16 at 19:23
  • See [this comment on the OWASP wiki](https://www.owasp.org/index.php/Talk:Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Confusion_About_Encrypted_Token_Pattern) that stemmed from another question about the encrypted token pattern. It seems to me that that section is sloppily written. – Neil Smithline Feb 25 '16 at 19:24

4 Answers4

27

TL;DR

A JWT, if used without Cookies, negates the need for a CSRF token - BUT! by storing JWT in session/localStorage, your expose your JWT and user's identity if your site has an XSS vulnerability (fairly common). It is better to add a csrfToken key to the JWT and store the JWT in a cookie with secure and http-only attributes set.

Read this article with a good description for more info https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage

You can make this CSRF protection stateless by including a xsrfToken JWT claim:

{ "iss": "http://galaxies.com", "exp": 1300819380, "scopes": ["explorer", "solar-harvester", "seller"], "sub": "tom@andromeda.com", "xsrfToken": "d9b9714c-7ac0-42e0-8696-2dae95dbc33e" }

So you will need to store the csrfToken in local/sessionStorage as well as inside the JWT (which is stored in a http-only and secure cookie). Then for csrf protection, verify that the csrf token in the JWT matches the submitted csrf-token header.

Scott Jungwirth
  • 371
  • 1
  • 3
  • 4
  • 1
    My read of this is that JWT with CSP (content-security-policy) would largely mitigate both XSS and csrf/xsrf when properly implemented. If that's not the case or there is evidence for or against this general conclusion please call it out. Thank you! – jimmont Mar 03 '17 at 19:55
  • This is a problematic situation. if an XSS attack is occured , then satan will have the localstorage which contains the CSRF value. This should be made with an input hidden value which should be submitted every time – Royi Namir May 03 '17 at 08:06
  • 2
    Regarding to of James Ward post: http://www.jamesward.com/2013/05/13/securing-single-page-apps-and-rest-services Cookies themselves are not the cause of CSRF vulnerabilities. It’s using the cookies on the server to validate a user that is the cause of CSRF. Just putting an authentication token into a cookie doesn’t mean it must be used as the mechanism to identify the user. What do you mean by NOT storing JWT in session? Taking above, there is no way to access cookie/session from malicious site where JWT is stored. – Rafał Łyczkowski May 07 '17 at 21:57
  • 4
    I don't think you need to store the CSRF token in the JWT payload, do you? Just store the `xsrf-token` in a secure cookie and let the javascript (`XMLHttpRequest`) set the `X-XSRF-TOKEN` header, then compare the cookie and header server-side. Since CSRF attacker can't set headers, it'll be fine. – Frondor Nov 12 '17 at 18:00
  • 2
    The linked article is very good in explaining the pros/cons of JWT in local/sessionStorage vs JWT in cookie, but I would not follow the last advice of storing the CSRF token in the JWT in a cookie. The CSRF token is used to protect the user from being tricked into sending an authenticated request (because the browser includes the JWT cookie with all requests). Including the CSRF token in the JWT would defeat it's purpose. Frondor has the right approach in his comment. – Dario Seidl Apr 01 '18 at 21:49
  • I came to this question after having read the stormpath post. Can you explain adding the token to the JWT makes it stateless or in other words where and how are your going to check that its value is correct? The article doesn’t mention that you need to use both cookies and local storage, as you do, but that aside, how are you going to get it into localStorage in the first place if it is a http only cookie? – Michael Nov 05 '18 at 01:07
  • If you have XSS attack, then you can also send malicious request to the server(your http only cookie will also be sent along with it) and the server won't suspect anything is wrong. JWT if short lived(e.g 10mins) are useless and so an attacker may not want to steal it to use later. IMO, saving JWT in localstorage is as secure as httponly cookie in case of XSS attack. You need to worry more about XSS attack than JWT/localstorage vs cookie. – bornfree Apr 01 '21 at 14:44
14

There is no need to encrypt the token or to include the nonce. The key properties of a CSRF token are that it is not predictable by an attacker, and, unlike cookies, that it is not added to every request by the browser. A cryptographically secure JWT stored in a hidden field meets both of these properties.

Note that you need to use JWT's that have user-unique data in them. Having an empty or generic JWT that isn't specific to the user will not provide you with security.

Neil Smithline
  • 14,621
  • 4
  • 38
  • 55
3

You could use a JWT as a CSRF token, but it would be needlessly complicated: a CSRF token doesn't need to contain any claims, or be encrypted or signed.

There is probably a misunderstanding about what JWT or CSRF tokens are used for (I was confused at first too). The JWT is an access token, used for authentication. The CSRF token, on the other hand, is used to protect the user from being tricked into sending a forged authenticated request. This is necessary when using a session or HTTP basic auth or storing the JWT in a cookie -- any authentication that is done automatically by the browser.

If you have such an authentication scheme, make sure that you

  • use proper HTTP verbs (GET must not have side-effects)
  • generate a new CSRF token on each request
  • include the CSRF token in a header or as an HTTP parameter on each PATCH, POST, PUT, and DELETE requests

Read, for example, how Spring handles CSRF.

Dario Seidl
  • 269
  • 2
  • 9
3

Please note that storing JWT in a cookie or localStorage is not the main concern. It is just as fine to store the JWT in a cookie as in localStorage, AS LONG AS the server does not look for the JWT in a cookie header. If you store JWT in a cookie called token but only accepts it on the server side in a Authorization: Bearer ... type header, you are just as safe from CSRF as you'd be when stored in localStorage, because the server side would ignore token sent in the Cookie header.

For this to work however your webapp needs to be built in a way that all valid requests are sent using JavaScript, where your JS reads the token from the cookie called token and attaches it to the outgoing request as a Authorization: Bearer ... type header.

So just note, it's not that much about where it is stored, it's more like about where it is being looked for on the server side.

sbnc.eu
  • 131
  • 2