Should JWT be stored in localStorage or cookie? [duplicate]
Asked Answered
D

4

159

For the purpose of securing REST API using JWT, according to some materials (like this guide and this question), the JWT can be stored in either localStorage or Cookies. Based on my understanding:

  • localStorage is subjected to XSS and generally it's not recommended to store any sensitive information in it.
  • With Cookies we can apply the flag "httpOnly" which mitigates the risk of XSS. However if we are to read the JWT from Cookies on backend, we then are subjected to CSRF.

So based on the above premise - it will be best if we store JWT in Cookies. On every request to server, the JWT will be read from Cookies and added in the Authorization header using Bearer scheme. The server can then verify the JWT in the request header (as opposed to reading it from the cookies).

Is my understanding correct? If so, does the above approach have any security concern? Or actually we can just get away with using localStorage in the first place?

Deciduous answered 15/1, 2016 at 18:38 Comment(3)
check this: cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessionsEnjambement
@lrn2prgrm as one should not use (stateless) JWT and (stateful) session semantics together.Gurglet
@Enjambement I am using JWT and I plan to use Authentication header "Bearer mytoken" on server-side to verify my jwt. My confusion is this: If I send the original jwt in a cookie on first login (sent from server to browser) with httpOnly flag, how can I extract the jwt from client-side to put in my Authentication header for subsequent requests? Wouldn't the httpOnly flag disallow me to extract the information from a cookie on client side ?Journalistic
V
91

I like the XSRF Double Submit Cookies method which mentioned in the article that @pkid169 said, but there is one thing that article doesn't tell you. You are still not protected against XSS because what the attacker can do is inject script that reads your CSRF cookie (which is not HttpOnly) and then make a request to one of your API endpoints using this CSRF token with JWT cookie being sent automatically.

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

Whether you store your JWT in a localStorage or you store your XSRF-token in not http-only cookie, both can be grabbed easily by XSS. Even your JWT in HttpOnly cookie can be grabbed by an advanced XSS attack.

So in addition of 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.

Vandenberg answered 25/5, 2016 at 3:21 Comment(5)
Can you clarify how a JWT in HttpOnly cookie can be grabbed? It can be used by XSRF if XSRF token is compromised by XSS, but can it really be grabbed itself?Simons
I know this is an old post, but I'd like to ask some questions... Does that mean well-crafted scripts can read both localStorage and cookie? If so, is it safe to assume that a JWT can possibly be stolen no matter where we store it in a browser? Then, I feel like relying solely on a JWT is very risky.Extinguisher
"Even your JWT in HttpOnly cookie can be grabbed by an advanced XSS attack." is false. Edited original post to correct this.Timepiece
"Even your JWT in HttpOnly cookie can be grabbed by an advanced XSS attack" I can imagine that someone grabs the cookie by sending it to his own server. For that he can use fetch with proper value of the credentials flag. The main problem here is the CORS protection but in some circumstances I think it is possible.Ninepins
"inject script that reads your CSRF cookie (which is not HttpOnly)" The default implementation of Html.AntiForgeryToken() in ASP.NET MVC uses an HttpOnly cookie for the CSRF token. I think you're still susceptible to certain XSS, but thought this was worth mentioning.Sirloin
D
37

A timely post from Stormpath has pretty much elaborated my points and answered my question.

TL;DR

Store the JWT in cookies, then either pass the JWT in the Authorization header on every request like I've mentioned, or as the article suggests, rely on the backend to prevent CSRF (e.g. using xsrfToken in case of Angular).

Deciduous answered 22/1, 2016 at 1:25 Comment(3)
Hello, Im not sure if this is correct but ,are there some particular disadvantages to using cookies to store jwt when you are implementing CrossOrigin for your controllers , that is a scene where my server app is located in a different place and we are calling the api from it in our client app which is located say in another city ? Isnt that why many webservice providers refrain from using cookies?Senhorita
CrossOrigin doesn't mean physical locations. It refers to requests coming from other domains. In .net core when you decide to use CORS you specify which domains you are going to allow; which headers you'll allow, etc.Extol
When I store the JWT in a cookie, how can I add it then in the Authorization header? I have no access to it...Phratry
F
28
  • Do not store your token in LocalStorage or SessionStorage, because such token can be read from javascript and therefore it is vulnarable to XSS attack.
  • Do not store your token in Cookie. Cookie (with HttpOnly flag) is a better option - it's XSS prone, but it's vulnarable to CSRF attack

Instead, on login, you can deliver two tokens: access token and refresh token. Access token should be stored in Javascript memory and Refresh token should be stored in HttpOnly Cookie. Refresh token is used only and only for creating new access tokens - nothing more.

When user opens new tab, or on site refresh, you need to perform request to create new access token, based on refresh token which is stored in Cookie.

I also strongly recommend to read this article: https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/

Fiasco answered 20/2, 2020 at 11:59 Comment(4)
Why the added complexity when you can just treat the refresh token as an access token? How is this approach more secure given I mark my access token as secure, samesite: strict, http-only?Warthman
it's not more secureHensley
if you have a malicious XSS in your page, the script can do a GET request to /refresh-token and access to a new jwt, then send it to remote server. So, in this scenario does this schema have the same vulnerability that localstorage?Spadefish
Why the added complexity when you can just treat the refresh token as an access token? @CharmingRobot how do you invalidated an access token? Without a refresh token, your access token should have a big life time so the user doesn't need to login every 5 minutes. That's why refresh token exists, so the user can logout removing the refresh token from your database, and in few minutes the access token will expired. That's why refresh tokens exists.Briticism
A
12

To help prevent CSRF attacks that take advantage of existing cookies, you can set your cookie with the SameSite directive. Set it to lax or strict.

This is still a draft and as of 2019 is not fully supported by all current browsers, but depending on the sensitivity of your data and/or your control over the browsers your users use, it may be a viable option. Setting the directive with SameSite=lax will allow "top-level navigations which use a 'safe'...HTTP method."

Antagonism answered 28/10, 2019 at 16:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.