Where to store JWT in browser? How to protect against CSRF?
Asked Answered
W

7

360

I know cookie-based authentication. SSL and HttpOnly flags can be applied to protect cookie-based authentication from MITM and XSS. However, more special measures will be needed to apply in order to protect it from CSRF. They are just a bit complicated. (reference)

Recently, I discover that JSON Web Token (JWT) is quite hot as a solution for authentication. I know the stuff about encoding, decoding, and verifying JWT. However, I don't understand why some websites/tutorials tell that there is no need for CSRF protection if JWT is used. I have read quite a lot and have tried to summarize the problems below. I just want someone to provide a bigger picture of JWT and clarify the concepts I misunderstood about JWT.

  1. If the JWT is stored in a cookie, I think it is the same as cookie-based authentication except that the server does not need to have sessions to verify the cookie/token. There is still a risk of CSRF if no special measure is implemented. Isn't JWT stored in a cookie?

  2. If the JWT is stored in localStorage/sessionStorage, then there is no cookie involved so don't need to protect against CSRF. The question is how to send the JWT to the server. I found here that it is suggested to use jQuery to send the JWT by HTTP header of ajax requests. So, only the ajax requests can do the authentication?

  3. Also, I found one more blog that points to use "Authorization header" and "Bearer" to send the JWT. I don't understand the method the blog talks about. Could someone please explain more about "Authorization header" and "Bearer"? Does this make the JWT transmitted by HTTP header of ALL requests? If yes, what about CSRF?

Workout answered 21/11, 2014 at 17:44 Comment(0)
W
125

JWT tokens are popular since they are used as the default token format in new authorization and authentication protocols like OAuth 2.0 and OpenID Connect.

When the token is stored in a cookie, the browser will automatically send it along with each request to the same domain and this is still vulnerable to CSRF attacks.

Bearer authentication is one of the authentication schemes defined in HTTP. It basically means that YOU stick the (JWT) token in the Authorization HTTP header of a request. The browser will NOT do this for you automatically, so it's not suitable for protecting your website. As the browser does not automatically add the header to your request, it is not vulnerable to a CSRF attack, which depends on your authentication info being submitted automatically to the original domain.

The bearer scheme is often used to protect web APIs (REST services) that are consumed via AJAX calls or from mobile clients.

Woehick answered 23/11, 2014 at 0:24 Comment(13)
So the only way to do Bearer authentication is via ajax request?Workout
@Timespace7 No, JWT tokens are also often used from native clients. OAuth 2.0 has flows specifically targetting native (mobile) clients. The thing they don't do is implicit browser authentication (like cookies or basic auth.).Woehick
Let me summarize, you say that: - If we dispatch our JWT to our user, we save it in a cookie and then whenever we make a call to our api add the Authoritzation header manually, we are protected against CSRF attacks?Shufu
I'm saying that if your API only retrieves the JWT token from the Authorization header, it is not vulnerable to CSRF. Any site or API that gets the token from a cookie needs CSRF mitigation.Woehick
Does this mean we can effectively store the jwt in a cookie and it will be secure if we send requests with it in the Authorization header?Correggio
@cameronjroe you can store it in your cookies but only if you don't use your cookies for authentication (you use your headers in this case)Corpulent
@Woehick As you said, JWT is not suitable for protecting website because browser will not auto send it. So does it mean JWT is not meant for authenticating requests originated from a browser? What if all requests are issued with ajax, which means my JS will always send the token explicitly rather than rely on browser sending the cookie implicitly.Laband
AJAX calls also originate from the browser. JWT tokens are mostly used to authenticate web APIs (serving data) vs cookies used to authenticate web apps (serving markup, images, css and JavaScript)Woehick
I was wondering also how to send the jwt token to the client without cookies right after the client has authenticated along side with html document of my web app. But until now I could not mange to find a natural feeling, easy way. Is there a way to this? I have asked it here: #34838251Shewmaker
any token stored in a cookie will be vulnerable to CSRF attack irrespective of the fact that you are sending it via authentication header, on the other hand if you send the token in your request header without using a cookie, you may be exposed to XSS attack (cross-site-script) which means that a peace of javascript code written by a hacker can run on your domain (on the client side) and that peace of code can steal the token and use it, I found this article helpful : linkSubaxillary
@Woehick It is not clear to me how / where you store the token to be able to access it to send in the Bearer authentication header. If it can be read from a cookie via javascript then it is vulnerable to XSS. Local storage is similarly vulnerable to XSS. Cookie authentication with a X-XSRF-TOKEN header as iman-sedighi mentions in his post sounds more secure (of course with other XSS protection and disabling TRACE requests, etc)Unblock
How is the client meant to "remember" the token after refreshing a page or clicking on a link to another page?Fugate
Client is not meant to remember the token. You can write it to cookie and retrieve it in page load but tokens are better suited for single page applications.Caliph
C
262

We need to store the JWT on the client computer. If we store it in a LocalStorage/SessionStorage then it can be easily grabbed by an XSS attack. If we store it in cookies then a hacker can use it (without reading it) in a CSRF attack and impersonate the user and contact our API and send requests to do actions or get information on behalf of a user.

But there are several ways to secure the JWT in cookies to not to be stolen easily (but there are still some advanced techniques to steal them). But if you wanna rely on LocalStorage/SessionStorage, then it can be accessed by a simple XSS attack.

So to solve the CSRF problem, I use Double Submit Cookies in my application.

Double Submit Cookies Method

  1. Store JWT in a HttpOnly cookie and used it in secure mode to transfer over HTTPS.

  2. Most of CSRF attacks have a different origin or referrer header with your original host in their requests. So check if you have any of them in the header, are they coming from your domain or not! If not reject them. If both origin and referrer are not available in the request then no worries. You can rely on the result of X-XSRF-TOKEN header validation results which I explain in the next step.

  3. While the browser will automatically supply your cookies for the domain of the request, there is one useful limitation: the JavaScript code that is running on a website cannot read the cookies of other websites. We can leverage this to create our CSRF solution. To prevent CSRF attacks, we must create an extra Javascript readable cookie which is called: XSRF-TOKEN. This cookie must be created when the user is logged in and should contain a random, un-guessable string. We also save this number in the JWT itself as a private claim. Every time the JavaScript application wants to make a request, it will need to read this token and send it along in a custom HTTP header. Because these operations (reading the cookie, setting the header) can only be done on the same domain of the JavaScript application, we can know that this is being done by a real user who is using our JavaScript application.

Angular JS makes your life easy

Fortunately, I am using Angular JS in our platform and Angular packages the CSRF token approach, making it simpler for us to implement. For every request that our Angular application makes of the server, the Angular $http service will do these things automatically:

  • Look for a cookie named XSRF-TOKEN on the current domain.
  • If that cookie is found, it reads the value and adds it to the request as the X-XSRF-TOKEN header.

Thus the client-side implementation is handled for you, automatically! We just need to set a cookie named XSRF-TOKEN on the current domain in server side and when our API got any call from the client, it must check the X-XSRF-TOKEN header and compare it with the XSRF-TOKEN in the JWT. If they match, then the user is real. Otherwise, it's a forged request and you can ignore it. This method is inspired by the "Double Submit Cookie" method.

Caution

In reality, you are still susceptible to XSS, it's just that attacker can't steal you JWT token for later use, but he can still make requests on your users' behalf using XSS.

Whether you store your JWT in the localStorage or you store your XSRF-token in not HttpOnly cookie, both can be grabbed easily by XSS. Even your JWT in an HttpOnly cookie can be grabbed by an advanced XSS attack like XST method.

So in addition to the Double Submit Cookies method, you must always follow best practices against XSS including escaping contents. This means removing any executable code that would cause the browser to do something you don’t want it to. Typically this means removing // <![CDATA[ tags and HTML attributes that cause JavaScript to be evaluated.

Read more here:

Centipoise answered 23/5, 2016 at 16:56 Comment(15)
Thanks for super detailed response. It it a good idea if I store the jwt in a httpOnly cookie and the signature of jwt as CSRF token in another cookie - X-XSRF-TOKEN, which is used automatically by Angular ? The reason to use signature in CSRF token is that it can be easily verifiedTrautman
@Trautman yes, this way is also an example of Double Submit Cookies Method. You can store the signature of JWT in the X-XSRF-TOKEN for angular. As reading the cookie and setting the header can only be done on the same domain of the JavaScript application, you are safe from CSRF. However you must verify the signature in server side to make sure JWT and the signature issued by you (and your secret key).Centipoise
Hi Iman, thanks for the response, is this XSRF-TOKEN setup prevents the replay attack as well? to me it does as the attacker can not replay the message because they cannot imitate the xsrf-token, if so then why JID is used specially for replay attack prevention?Subaxillary
@AranDehkharghani yes I guess it prevents replay attack especially if you change JWT and expire the previous JWT every time it used by API. it means your JWT will become like a one-time password (OTP). You can use JWT in different ways depends on how much do you care about security in your platform.Centipoise
@ImanSedighi thanks for the response, if I implement OTP then it implies I only can use it for one further request, now what could be the solution if I need to send concurrent requests from javascript, maybe returning a JWT pool to the client when a request receives? doesn't sound very elegantSubaxillary
As you mentioned, if a website is vulnerable to XSS, then it is just a matter of time before the user is exploited. It seems like we are trading significant complexity for a very small increase in security.Predatory
@Predatory You must take care of XSS and XSRF attacks to protect your JWT. I don't agree that you are trading significant complexity for a very small increase in security. If security matters, then you need to put all efforts to not to have XSS vulnerabilities. This method is designed to protect your token from XSRF attacks. but it doesn't mean that you can ignore XSS vulnerabilities.Centipoise
@ImanSedighi I wasn't clear, by storing the jwt in a cookie you are adding complexity and you now have to protect against XSRF. So why not just use local storage with short life tokens and concentrate on preventing XSS?Predatory
There is a downside using xsrf-token, you have to send it as an X-XSRF-TOKEN header , adding a header is only possible if you can read the cookie with JS, you can read the cookie with JS only if it's not HTTP cookie, if it's HTTP cookie, you can't do CORS so your app has to be on the same domain. Did you found a solution for that ? That's why I removed csrf protection from my REST api.Ranique
OWASP are a great recourse for web vulnerabilities owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)Shirting
@ImanSedighi Does it work if we simply store the JWT in a cookie, and require all XHR requests to include the JWT in the authorization header by using Javascript to read from the cookie, and change the server to read from the header (INSTEAD OF THE COOKIE). Storing in cookie protect us from XSS by nature, and to eliminate CSRF, we read the cookie using our own JS code and attach the JWT to the header. I don't see where the XSRF-TOKEN comes into play necessarily, but I bet I'm missing sth..Cilo
^ I missed a critical point above when I said cookie protect us from XSS by nature. It was important that the cookies are HTTP-only for it to be safe from XSS. So ignore what I said above.Cilo
@ImanSedighi On a second thought, isn't the double submit method you suggested still vulnerable to an XSS followed by a CSRF? To be clear, a hacker can use XSS to retrieve the XSRF-TOKEN cookie which is not HTTP-only, then he can use CSRF on his website to send AJAX requests with XSRF-TOKEN as one of the headers to the vulnerable host. In this case, the cookies will also be sent along as the vulnerable host's domain matches the cookies domain. What do you mean by "it's just that attacker can't steal you[r] JWT token for later use"? Why not?Cilo
I completely disagree. An origin header can easily be spoof with a proxy wireshark. The right way of mitigating CSRF is with another unknown(!!!!!!!!!) value (to the attacker) which is like GUID input sent initially to the form. ( hidden field.Were
@Royi Namir: Spoofing by Wireshark should not be a concern if you use a $10 SSL certificate! If the security of the website is important then you should encrypt the data and use HTTPS protocol.Centipoise
W
125

JWT tokens are popular since they are used as the default token format in new authorization and authentication protocols like OAuth 2.0 and OpenID Connect.

When the token is stored in a cookie, the browser will automatically send it along with each request to the same domain and this is still vulnerable to CSRF attacks.

Bearer authentication is one of the authentication schemes defined in HTTP. It basically means that YOU stick the (JWT) token in the Authorization HTTP header of a request. The browser will NOT do this for you automatically, so it's not suitable for protecting your website. As the browser does not automatically add the header to your request, it is not vulnerable to a CSRF attack, which depends on your authentication info being submitted automatically to the original domain.

The bearer scheme is often used to protect web APIs (REST services) that are consumed via AJAX calls or from mobile clients.

Woehick answered 23/11, 2014 at 0:24 Comment(13)
So the only way to do Bearer authentication is via ajax request?Workout
@Timespace7 No, JWT tokens are also often used from native clients. OAuth 2.0 has flows specifically targetting native (mobile) clients. The thing they don't do is implicit browser authentication (like cookies or basic auth.).Woehick
Let me summarize, you say that: - If we dispatch our JWT to our user, we save it in a cookie and then whenever we make a call to our api add the Authoritzation header manually, we are protected against CSRF attacks?Shufu
I'm saying that if your API only retrieves the JWT token from the Authorization header, it is not vulnerable to CSRF. Any site or API that gets the token from a cookie needs CSRF mitigation.Woehick
Does this mean we can effectively store the jwt in a cookie and it will be secure if we send requests with it in the Authorization header?Correggio
@cameronjroe you can store it in your cookies but only if you don't use your cookies for authentication (you use your headers in this case)Corpulent
@Woehick As you said, JWT is not suitable for protecting website because browser will not auto send it. So does it mean JWT is not meant for authenticating requests originated from a browser? What if all requests are issued with ajax, which means my JS will always send the token explicitly rather than rely on browser sending the cookie implicitly.Laband
AJAX calls also originate from the browser. JWT tokens are mostly used to authenticate web APIs (serving data) vs cookies used to authenticate web apps (serving markup, images, css and JavaScript)Woehick
I was wondering also how to send the jwt token to the client without cookies right after the client has authenticated along side with html document of my web app. But until now I could not mange to find a natural feeling, easy way. Is there a way to this? I have asked it here: #34838251Shewmaker
any token stored in a cookie will be vulnerable to CSRF attack irrespective of the fact that you are sending it via authentication header, on the other hand if you send the token in your request header without using a cookie, you may be exposed to XSS attack (cross-site-script) which means that a peace of javascript code written by a hacker can run on your domain (on the client side) and that peace of code can steal the token and use it, I found this article helpful : linkSubaxillary
@Woehick It is not clear to me how / where you store the token to be able to access it to send in the Bearer authentication header. If it can be read from a cookie via javascript then it is vulnerable to XSS. Local storage is similarly vulnerable to XSS. Cookie authentication with a X-XSRF-TOKEN header as iman-sedighi mentions in his post sounds more secure (of course with other XSS protection and disabling TRACE requests, etc)Unblock
How is the client meant to "remember" the token after refreshing a page or clicking on a link to another page?Fugate
Client is not meant to remember the token. You can write it to cookie and retrieve it in page load but tokens are better suited for single page applications.Caliph
H
91

Now in 2020, simply store the JWT token in a cookie with SameSite=strict to defeat CSRF. Of course, keep secure and httpOnly too.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite

Heard answered 14/8, 2020 at 5:48 Comment(4)
The only problem with this method, you can't use it with Safari 14+ and will NOT be able to use it with Chrome in 2022 when you need cross-site cookies for web widgets.Glimpse
@beytarovski Can you elaborate on the upcoming issue and what use cases that would impact?Volant
@Volant Simply httpOnly cookies don't/won't work at all if you develop a 3rd party application like Intercom. Because your application will want to keep session of visitors in the client website on page refreshes. Because cookies can't be stored in your server, it isn't a solution anymore (webkit.org/blog/10218/full-third-party-cookie-blocking-and-more).Glimpse
The SameSite Cookie attribute is an additional layer of defence for CSRF attacks, but does not entirely mitigate it. OWASP, still to this day, recommends to use CSRF token as the most complete CSRF Mitigation technique. That is because SameSite can be entirely bypassed with XSS or HTML injection vulnerabilities on the target domain, or its subdomains or its sibling domains. Subdomain takeover (SDTO) and MIT attacks also bypass the SameSite restriction.Tricolor
K
24

Store your access token in memory and store your refresh token in the cookie

Why is this safe from CSRF?

Although a form submit to /refresh_token will work and a new access token will be returned, the attacker can't read the response if they're using an HTML form. To prevent the attacker from successfully making a fetch or AJAX request and read the response, this requires the Authorization Server's CORS policy to be set up correctly to prevent requests from unauthorized websites.

You can read more about it here:

https://dev.to/cotter/localstorage-vs-cookies-all-you-need-to-know-about-storing-jwt-tokens-securely-in-the-front-end-15id

Kilocalorie answered 26/8, 2020 at 8:49 Comment(6)
This is great advice and it applies more broadly than JWT, but to any oauth token format. It's the only security model that I can think of that works well for web applications that make use of rest-api endpoints.Bulletin
cors is not a problem if you use app to make a request :vTriacid
@Triacid You're talking about attaching the cookie to a request by a malicious user. Consider that when you do store the access token in memory or send it through http request, even if someone tries and misuses the refresh token by attaching the http-only cookie to their manipulated request it won't be a problem because for doing any action they need the access token that they don't get it. That's obvious it's achieved by a manipulated request to the website, not inside the app. Inside an app you can access CSRF token too. Please read the full article to see the exact point.Kilocalorie
Best answer without a doubt!Probity
Just in case, does this mean we do not require x-xsrf-token anymore?Probity
Other important point from the site : "...This means that the access token will be gone if the user switches tabs or refresh the site. That's why we have the refresh token."Eternity
C
17

Another angle to the whole issue of storing JWTs:

  1. JWTs should never be stored in your localStorage
  2. In fact, they shouldn't even be stored in your cookies, unless you are able to implement very strict CSRF protection

Checkout this for motivation

  • JWT as an id_token is like your user credentials
  • JWT as an access_token is like your session token

The most secure option is in-memory. Checkout this for a deep dive

Cordes answered 19/3, 2020 at 11:27 Comment(5)
in-memory is still susceptible to XSS, is it not? If so, then CSRF protected cookies seems like the only secure solution.Glossematics
why I can not save cookie in browser (chrome)? I get a refresh token from server, I can see it returned in network tab but in Application\Storage\Cookies I can not see it. HttpOnly is true, SameSite "none" can not set.Trujillo
This answer needs loads more information, why is in memory more"secure" why are the other options not "secure". In reality there is no "secure" storage. So if your going to make bold claims about what people shouldn't do, you need to provide facts to back these claims up. And no, a link to a web page doesn't count.Infeudation
There should be more reasoning as to what is the benefit of the proposed solution over the others, not just external links.Laris
This answer is nonsense. By all means, if you don't need to store something, don't. But if you do, do. That's all there is to it. Local Storage is not inherently insecure, and any answer claiming that you should "never" use it for data like auth JWTs is missing the point.Ashcan
S
16

In web browser, you can store JWT in local/session storage or in cookie. Both have vulnerabilities. You can choose the one you prefer, but you should take the security as a whole to be secured and processes should be well designed. If you prevent only against XSRF and XSS it will not help you. This is short answer to your question.

First you want to prevent user data to be stolen. Very problematic is XSS attack. If you use storage, attacker can steal token - send token to his server and make requests to steal user data. If you use httpOnly cookie, he cannot steal token, but he can send requests (browser includes cookies, if script is on the same domain), read responses and send user data to his server. The result is same.

To prevent sending data to servers with different domain you can use Content-Security-Policy header. I recommend to study all security headers and web security. Good resource is OWASP. This forum is not about writing many pages.

XSRF (CSRF)

If you use cookies, then application is vulnerable to this attack.

How to prevent it:

Set httpOnly, secured and SameSite=strict flags. You can also use second cookie XSRF-TOKEN without httpOnly and send its value in header X-Xsrf-Token along with this cookie. But this is solved by SameSite flag if browser supports it.

XSS

Both storage and cookies are vulnerable to XSS in some meaning. With javascript code you can read storage and you can send requests to server with cookies included by browser as you are on the same domain. If you use user inputs, you should escape/sanitize them. You can also use header x-xss-protection. The most problematic is malicious code in 3rd party js libs as you cannot escape it and it runs on the same domain. You can prevent user data to be stolen by your mistake, but such code can cause different problems to your application and users.

Sectionalism answered 29/1, 2021 at 14:54 Comment(0)
Z
2

JWT should be stored in cookies. You can use httponly and secure flags depending on your requirements.

To protect from CSRF samesite cookie attribute can be set to strict if it generally fits your application - it will prevent logged-in users of your site to follow any link to your site from any other site.

IMHO, CSRF protection should be done separately from authentication using the Double Submit Cookie technique:

On the server, you generate a random string (csrf_token) and set it in the cookie. On the client, you take the cookie value and submit it as a header value. On the server, you compare values from the cookie and the header. They should match.

Ziegfeld answered 29/12, 2022 at 20:13 Comment(2)
Besides that, what if - when a user lands on a website and denies the "accept cookies" popup? Would that whole idea of storing JWT in a secure cookie not become a problem? What would you do in that case?Intercellular
@ShadowGames. If the user doesn't want cookies, that means she does not want to be tracked. You still can use cookies in the authentication process. But I am not a lawyer.Ziegfeld

© 2022 - 2024 — McMap. All rights reserved.