Is it safe to store a JWT in localStorage with ReactJS?

158,369

Solution 1

In most of the modern single page applications, we indeed have to store the token somewhere on the client side (most common use case - to keep the user logged in after a page refresh).

There are a total of 2 options available: Web Storage (session storage, local storage) and a client side cookie. Both options are widely used, but this doesn't mean they are very secure.

Tom Abbott summarizes well the JWT sessionStorage and localStorage security:

Web Storage (localStorage/sessionStorage) is accessible through JavaScript on the same domain. This means that any JavaScript running on your site will have access to web storage, and because of this can be vulnerable to cross-site scripting (XSS) attacks. XSS, in a nutshell, is a type of vulnerability where an attacker can inject JavaScript that will run on your page. Basic XSS attacks attempt to inject JavaScript through form inputs, where the attacker puts <script>alert('You are Hacked');</script> into a form to see if it is run by the browser and can be viewed by other users.

To prevent XSS, the common response is to escape and encode all untrusted data. React (mostly) does that for you! Here's a great discussion about how much XSS vulnerability protection is React responsible for.

But that doesn't cover all possible vulnerabilities! Another potential threat is the usage of JavaScript hosted on CDNs or outside infrastructure.

Here's Tom again:

Modern web apps include 3rd party JavaScript libraries for A/B testing, funnel/market analysis, and ads. We use package managers like Bower to import other peoples’ code into our apps.

What if only one of the scripts you use is compromised? Malicious JavaScript can be embedded on the page, and Web Storage is compromised. These types of XSS attacks can get everyone’s Web Storage that visits your site, without their knowledge. This is probably why a bunch of organizations advise not to store anything of value or trust any information in web storage. This includes session identifiers and tokens.

Therefore, my conclusion is that as a storage mechanism, Web Storage does not enforce any secure standards during transfer. Whoever reads Web Storage and uses it must do their due diligence to ensure they always send the JWT over HTTPS and never HTTP.

Solution 2

I know this is an old question but according what @mikejones1477 said, modern front end libraries and frameworks escape the text giving you protection against XSS. The reason why cookies are not a secure method using credentials is that cookies doesn't prevent CSRF when localStorage does (also remember that cookies are accessible by JavaScript too, so XSS isn't the big problem here), this answer resume why.

The reason storing an authentication token in local storage and manually adding it to each request protects against CSRF is that key word: manual. Since the browser is not automatically sending that auth token, if I visit evil.example and it manages to send a POST http://example.com/delete-my-account, it will not be able to send my authn token, so the request is ignored.

Of course httpOnly is the holy grail but you can't access from reactjs or any js framework beside you still have CSRF vulnerability. My recommendation would be localstorage or if you want to use cookies make sure implemeting some solution to your CSRF problem like Django does.

Regarding with the CDN's make sure you're not using some weird CDN, for example CDN like Google or bootstrap provide, are maintained by the community and doesn't contain malicious code, if you are not sure, you're free to review.

Solution 3

Basically it's OK to store your JWT in your localStorage.

And I think this is a good way. If we are talking about XSS, XSS using CDN, it's also a potential risk of getting your client's login/pass as well. Storing data in local storage will prevent CSRF attacks at least.

You need to be aware of both and choose what you want. Both attacks it's not all you are need to be aware of, just remember: YOUR ENTIRE APP IS ONLY AS SECURE AS THE LEAST SECURE POINT OF YOUR APP.

Once again storing is OK, be vulnerable to XSS, CSRF,... isn't

Solution 4

A way to look at this is to consider the level of risk or harm.

Are you building an app with no users, POC/MVP? Are you a startup who needs to get to market and test your app quickly? If yes, I would probably just implement the simplest solution and maintain focus on finding product-market-fit. Use localStorage as its often easier to implement.

Are you building a v2 of an app with many daily active users or an app that people/businesses are heavily dependent on. Would getting hacked mean little or no room for recovery? If so, I would take a long hard look at your dependencies and consider storing token information in an http-only cookie.

Using both localStorage and cookie/session storage have their own pros and cons.

As stated by first answer: If your application has an XSS vulnerability, neither will protect your user. Since most modern applications have a dozen or more different dependencies, it becomes increasingly difficult to guarantee that one of your application's dependencies is not XSS vulnerable.

If your application does have an XSS vulnerability and a hacker has been able to exploit it, the hacker will be able to perform actions on behalf of your user. The hacker can perform GET/POST requests by retrieving token from localStorage or can perform POST requests if token is stored in a http-only cookie.

The only down-side of the storing your token in local storage is the hacker will be able to read your token.

Solution 5

I’m disturbed by all the answers that suggest not to store in local storage as this is susceptible to an XSS attack or a malicious library. Some of these even go into long-winded discussions, even though the answer is pretty small/straightforward, which I’ll get to shortly.

Suggesting that is the equivalent of saying “Don’t use a frying pan to cook your food because if you end up drunk one night and decide to fry, you’ll end up burning yourself and your house”. If the jwt gets leaked due to an XSS attack or malicious library, then the site owner has a bigger problem: their site is susceptible to XSS attacks or is using a malicious library.

The answer: if you’re confident your site doesn’t have those vulnerabilities, go for it.

Ref: https://auth0.com/docs/security/data-security/token-storage#browser-local-storage-scenarios

Share:
158,369

Related videos on Youtube

Kai - Kazuya Ito
Author by

Kai - Kazuya Ito

I'll fill up About me soon.

Updated on March 12, 2022

Comments

  • Kai - Kazuya Ito
    Kai - Kazuya Ito about 2 years

    I'm currently building a single page application using ReactJS.

    I read that one of the reasons for not using localStorage is because of XSS vulnerabilities.

    Since React escapes all user input, would it now be safe to use localStorage?

    • Praneet Rohida
      Praneet Rohida almost 7 years
      prefer Session Storage
    • Arman Charan
      Arman Charan over 5 years
    • benbotto
      benbotto over 4 years
      "It's recommended not to store any sensitive information in local storage." -OWASP "store them in memory without any persistence" -Auth0
    • DauleDK
      DauleDK almost 4 years
      I think Auth0 might have changed their perspective on this - because I can't find the above quote in the provided link
    • NotTheDr01ds
      NotTheDr01ds over 3 years
      To be fair, @DauleDK, I think that particular quote is missing not because Auth0 now thinks that it is safe to do so, but quite the opposite -- If you read that page, it now appears that they advise that the token never be persisted in a client-side only solution, regardless of the persistence technique used: "If you have a SPA with no corresponding backend server, your SPA should request new tokens on login and store them in memory without any persistence. To make API calls, your SPA would then use the in-memory copy of the token."
    • DauleDK
      DauleDK over 3 years
      That's a fair point @NotTheDr01ds - if storing JWT's in a SPA care must be taken. But the real question seems to be if cookies should have been used, as a better alternative.
  • Admin
    Admin almost 7 years
    If I don't plan on using cdns will it be safe then?
  • Alex Lyalka
    Alex Lyalka almost 7 years
    cant get: how HttpOnly can protect you from CSRF ?
  • Ivan
    Ivan almost 7 years
    @AlexLyalka Didn't mean to say that HttpOnly prevents from CSRF, rather than all cookie flags together can protect from XSS and CSRF. SameSite provides some protection, preventing the cookies from being sent to a site different from origin. Although I just checked and support for that flag is very low. It's also possible to avoid CSRF with a separate encrypted token with some user identification, which is checked on server.
  • SuperLemon
    SuperLemon over 6 years
    So if I'm understanding you correctly, you recommend cookies? Just to make sure. Thanks!
  • Kaloyan Kosev
    Kaloyan Kosev over 6 years
    Yes. I recommend cookies because of the additional security they provide, and the simplicity of protecting against CSRF with modern web frameworks. Web Storage (localStorage/sessionStorage) is vulnerable to XSS, has a larger attack surface area, and can impact all application users on a successful attack.
  • Alex Cavazos
    Alex Cavazos over 6 years
    This is why it is secure to do the following: - Store the JWT in a cookie so that it can't be retrieved from XSS - Store a CSRF token in localStorage so it can't be retrieved from CSRF
  • mljohns89
    mljohns89 over 6 years
    I think you have these mixed up? Modern web frameworks have strong built in defenses for XSS. But not so much for xsrf. The best defense for xsrf is to avoid using cookies completely. Local storage is sandboxed to a specific domain, which means an attackers domain can't access it. Web frameworks defend against xss by automatically encoding and sanatizing user input. See angular.io/guide/security
  • JounceCracklePop
    JounceCracklePop almost 6 years
    You bring up a good point: if your site runs a malicious script, it's game over anyway. They can just bind keydown events to inputs of type password and steal your user's authentication information that way (which is much, much worse than stealing a JWT auth token). Storing JWTs in localStorage does little to increase the already immense possible damage from XSS.
  • spechter
    spechter almost 6 years
    If "you recommend cookies [instead]", then, can I recommend you say that somewhere in the answer? Rather than just in the comments?
  • Sergiu Sandrean
    Sergiu Sandrean over 5 years
    @mikejones1477: As the answer says "Modern web apps include 3rd party JavaScript libraries" which means you can never be 100% sure that you're defended against XSS attacks, or which means you can't be sure that your localStorage can't be accessed by an attacker. The best way to stay secure is to store your token in the cookie and implement a CSRF protection (which most backend frameworks offer nowadays).
  • Vlad
    Vlad over 5 years
    The author of the article never made a distinction between XSS on sites served via a CDN or directly from a central server. Wouldn't your explanation here also apply generally, not just for CDNs?
  • Borja Alvarez
    Borja Alvarez over 5 years
    I'm here a bit late, just reading these topics now and I'm confused about one thing, a lot of people talk about u're protected with an http only cookie if you are comprimised with Xss, but, if you have xss the atacker does not need to steal you anything, he can simple make a post from the page to impersonate you using that cookie (even if he cant steal it). Am I missing something???
  • Merv
    Merv about 5 years
    @Borja Alvarez .. Yes you are that making of a post is protected by usage of CSRF protection. Without it yes you are right.
  • Borja Alvarez
    Borja Alvarez about 5 years
    Well, if someone can execute code in your web, cant he simply make a post to ur web in the name of your user? Ok, he cant get your http only cookies, but he can make calls using those cookies, so I still cant see the point
  • Paul Griffin
    Paul Griffin about 5 years
    @Merv With or without CSRF protection, if you fall victim to XSS the attacker can send a post request. If they can run their code on your site they have no need to send the request cross site. I'm all in favour of storing JWT in a HttpOnly secure flag cookie, because why not? But really, if an XSS attack gets through the attacker can do what they like, send post requests, steal passwords, etc. The fact they can't read the JWT is small comfort.
  • Merv
    Merv about 5 years
    @BorjaAlvarez I agree if an attacker can run their code on your site then they would be able to do harm. But what they will not be able to do is read your JWT token. So they will always need your site up and running to do what they want to do. So in a way they will be at the mercy of your site. But if they are able to read your JWT token then its a party because you (server, company) won't ever know that its not the user who is sending requests because they are sending a valid JWT-token. And yes CSRF protection protect you only when your site is XSS safe.
  • miphe
    miphe almost 5 years
    Not sure why you would say you're still vulnerable to CSRF while using cookies. Using a cookie with the flags HttpOnly SameSite=strict and secure, will keep the info you set in the cookies safe. Then against XSS, you simply make sure your JavaScript isn't aware of any authentication related data, like tokens and passwords (meaning, don't store them in Web Storage) - if you import a malicious script, that script will not have access to sensitive data. Yes, you will not have access to the token through JS either, but that really shouldn't be a problem.
  • Mauricio Cortazar
    Mauricio Cortazar almost 5 years
    @miphe that's what i said. But the OP is asking for a way to access from javascript. Here i'm just explaining what is the best way to store a token accessible from js.
  • benbotto
    benbotto over 4 years
    You're not stuck with "a total of 2 options." If you must store tokens in your user agent (which you shouldn't be doing), then store them in memory without persistence. That's Auth0's recommendation (auth0.com/docs/security/store-tokens#if-no-backend-is-prese‌​nt) OWASP specifically advises against storing tokens in web storage: cheatsheetseries.owasp.org/cheatsheets/…
  • benbotto
    benbotto over 4 years
    @BorjaAlverez There's a big difference. Yes, via XSS someone could make requests on behalf of logged-in user, but compromising a token is worse. For example: the token may provide access to APIs that the client application doesn't use; the token may have other information about the user (email address, profile, and grants); the token could be used in replay attacks against your application; the token could be passed as an id_token_hint to an OIDC auth server; the token provides an attacker information about the cipher that was used to sign it; etc.
  • Kidali Kevin
    Kidali Kevin over 4 years
    @HassanAlthaf you are missing the point here, there will never be a 100% secure proof app, It’s just that, you’re reducing the attack surface and at least env file will not be published on github directly. Also, the bundled code will be obfuscated and mangled, making it hard for the attackers to find them.
  • codez
    codez over 4 years
    Private key is not supposed to be exposed. You would compromise the entire API.
  • Kidali Kevin
    Kidali Kevin over 4 years
    From my experience If you did things intelligently and correctly, your private key won't be exposed in your production build, if so, screenshot to support this or even a url.
  • Evans M.
    Evans M. about 4 years
    If you store your token in memory what happens if the user refreshes the page? Do you ask them to login again? Also what about open one of the pages in a new tab?
  • Fabian
    Fabian over 3 years
    Your example will expose the private key with compilation hard coded into the code. That's really unsecure.
  • Lo-Tan
    Lo-Tan over 3 years
    Encrypting JWTs is for transmitting sensitive data inside of a JWT. A JWT is a user session though, and even encrypted, still represents a valid user session because it is decrypted when it reaches the back-end. If it's bad to put them into localStorage unencrypted, it's just as bad to put them into localStorage encrypted. @Fabian As long as the secret is accessed by invoking the process.env (which seems to be inferred by the comment in the value), then this value would not be exposed in build artifacts.
  • Rehmat
    Rehmat over 3 years
    Is CSRF relevant here? I don't think so. Just XSS should be the point of interest here. And if an XSS attack is allowed, a session can be hijacked even if cookies are in use. Please correct me if I'm wrong. So in my opinion, localStorage is as secure as the traditional cookies.
  • Jenuel Ganawed
    Jenuel Ganawed over 3 years
    i think, even if you encrypt the key. Attackers might use them by attaching it to the link that needs a key.
  • Fabian
    Fabian over 3 years
    @Lo-Tan when the frontend code gets compiled the process.env variable will be resolved and be put into the code. If it wouldn't be resolved, the browser wouldn't know about his env variable. So his private key is exposed this way.
  • Lo-Tan
    Lo-Tan over 3 years
    @Fabian I guess I would never imagine sending a private key to the client, and somehow didn't notice the localStorage mentioned multiple times (code and comment). Yikes! Yeah - don't do this folks.
  • Juan Carrey
    Juan Carrey over 3 years
    Unless auth method is single use code, or passwordless or 2FA
  • Juan Carrey
    Juan Carrey over 3 years
    Any npm package you include is js running on your web.
  • Juan Carrey
    Juan Carrey over 3 years
    If private key is some sort of user password, but then again, browsers already remember user/pwds anyway
  • ruwan800
    ruwan800 over 3 years
    You can store the token in cookies (to avoid XSS) and read it using javascript and send it in the headers (to avoid CSRF). Got it from here. security.stackexchange.com/a/201228
  • Dmitry Verhoturov
    Dmitry Verhoturov over 3 years
    @JuanCarrey user password stored securely by the browser is nothing like local storage variable which can be read by any JS executed on that domain.
  • Juan Carrey
    Juan Carrey over 3 years
    I mean that there is no need to reinvent de wheel by creating a new password mechanism (private key on the code) when the browsers already store user/passwords for us. @DmitryVerhoturov
  • feskr
    feskr about 3 years
    When going for localStorage. I suggest adding Content-Security-Policy headers as an extra security layer to minimize the risk of packages trying to steal your tokens.
  • limeandcoconut
    limeandcoconut about 3 years
    Clearly the best solution is to store your creds in both localStorage and cookies. That way the cookie is protected from XSS and the Web Storage is protected from CSRF! CHECKMATE! (This is sarcasm. Don't do it.)
  • FINDarkside
    FINDarkside almost 3 years
    @ruwan800 You cannot do them both at the same time. If your cookie is httpOnly you can't read it with js. If it's no httpOnly, attacker can read it as well.
  • user2484998
    user2484998 almost 3 years
    What sense has to store jwt in cookie? If then in my web app I would like to request in another server service. That service can't decrypt that cookie in order to get jwt.
  • ospider
    ospider almost 3 years
    Nowadays, just set Cookie SameSite as strict to prevent CSRF.
  • ICW
    ICW about 2 years
    @JounceCracklePop This comment convinced me that worrying about what happens when an XSS attack happens is probably not a good use of my attention. If an XSS attack is successful, nothing you can do will protect your users. So, to spend time trying to defend yourself after a compromise is a waste of effort, it's much better to focus on preventing the XSS attack in the first place.
  • ICW
    ICW about 2 years
    If there is a successful XSS attack there is nothing you can do to ensure your user's security. IF the attacker can run javascript they can record your users input with event handlers (this cannot be stopped at this point), allowing them to get ahold of the users credentials, which is actually much worse than if they were to simply get ahold of the web token because now they can use those credentials to login to the users accounts on other application (probably). Preventing an XSS attack in the first place should be the goal, preparing for an attack is futile.
  • Leponzo
    Leponzo about 2 years
  • Sabaoon Bedar
    Sabaoon Bedar almost 2 years
    what if we encrypt the JWT token with a secret key and then store it in the local storage it will provide an extra layer to the security, and then we decrypt it where ever we need it.
  • j5423951
    j5423951 almost 2 years
    @SabaoonBedar How do you decrypt that data client side without exposing the private key in the client side code that hackers can read?
  • jacob
    jacob almost 2 years
    @ICW XSS attacks aren't the only thing to worry about. A malicious library or cdn script could steal your access token from localstorage. Keeping it in an httpOnly cookie prevents all javascript from stealing your token.
  • jacob
    jacob almost 2 years
    I don't agree with this perspective which seems to be saying that if you're vulnerable to this attack then it's game over anyways. If you keep your access token outside of javascript, then your access tokens can't be stolen from malicious javascript. This limits the damage that can be done by an attacker. Maybe they're not just trying to steal your access token but are stealing your keyboard input to try and get your passwords or secret question answers. But maybe you're using Oauth and they aren't entering that info into your site to begin with. The point is to reduce the potential damage.