Generator-jhipster: Is it OK to store the JWT in local/session storage ?

Created on 15 Apr 2016  ·  20Comments  ·  Source: jhipster/generator-jhipster

This makes it vulnerable to XSS attacks.
See this blog
In micro-service architecture we should probably store it in a gateway session Cookie.

area

All 20 comments

@jhipster/developers any opinion on this ?

Here are some links about this question :
http://stackoverflow.com/questions/35313384/jwt-cookies-https-csrf
« However, making the session token cookie httpOnly and secure still leaves you vulnerable to CSRF attacks”
There are techniques for preventing CSRF attacks. I would recommend reading this OWASP page on CSRF attacks and preventions.
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)

http://security.stackexchange.com/questions/120160/is-jwt-in-cookies-with-any-csrf-solution-just-as-vulnerable-to-xss-as-jwt-in-loc

Yes, we already have CSRF protection in JHipster session based auth and it should of course also be used here.

I'm surprised we are vulnerable to those attacks, and we first need to check if that's correct or not.
Then, as @cbornet says, we have a built-in protection for session-based auth, so we would just need to reuse it.

Actually, this is the continuation of #2584 discussion : as @sdoxsee pointed out you can't have a "remember-me" with OAuth2, former x-auth, or JWT without a server-side proxy (which would probably be the gateway in MSA) or else you are vulnerable to XSS

@cbornet tough question :) it's complicated to me...

@dsyer seems to advocate for almost always using the session: https://spring.io/guides/tutorials/spring-security-and-angular-js/#_but_what_about_my_custom_token_implementation_it_s_stateless_look
In this case it doesn't seem to be so much to protect against XSS explicitly as it is to leverage existing security infrastructure vs "roll-your-own".

Then there's https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/ where the case is made for authentication with token's over cookies (but with plenty of controversy in the comments)

Dominick Baier, an expert in this area and the one from whom I first heard about Open ID Connect a couple years ago, seems to reaffirm in the face of http://alexbilbie.com/2014/11/oauth-and-javascript/ that the implicit flow (which, like JWT or OAuth2 "resource owner password credentials flow" that jhipster currently uses, exposes the access token to the browser...thus XSS) is fine: https://leastprivilege.com/2016/01/17/which-openid-connectoauth-2-o-flow-is-the-right-one/#comment-75583 (also see the article in which the comments are made).

I personally like leveraging spring security's session because it

  • is relatively simple to setup with spring-boot's auto-configuration
  • doesn't require you to re-authenticate after x-minutes of signing in but keeps the session alive as you use it
  • doesn't involve rolling-your-own authentication strategy and the risks involved
  • keeps the token out of the browser (i.e. avoid XSS)
  • paves the way for Single Sign On

On the other hand, if you use the session, I don't think your backend API is mobile-friendly. With microservices, there is the BFF (Backend For Frontend: see http://samnewman.io/patterns/architectural/bff/) pattern where you'd have a different gateway for each frontend (be it browser, IOS, Android, etc.). So, in the case that it's not easily feasible to use the session (e.g. mobile), have a new gateway for that frontend that is token-based.

To answer shortly: no, it's not safe, you are vulnerable to CSRF attacks, as the attacker can use the localstorage to get access to the token.

  • If you use a session, you will remove this kind of attack: this is what we do with our session-based authentication, so it would be trivial to re-use it. However, the goal here is to be stateless, as it's much easier to scale.
    Spring Session could be a solution, but it's really complex to set up (you need something like a Redis server to replicate sessions), so I don't think we should generate something like this, as users will have a hard time to configure it afterwards (unless they use Docker, but not everybody does)
  • If you stay stateless, which is the goal of the JWT option, I think that as soon as you use a remember-me mechanism you will get the issue. So if you don't want the issue, remove the remember-me.

@cbornet I'm closing this as I think we have the answer!

But then, shouldn't we remove the remember-me with JWT ?

https://github.com/jhipster/generator-jhipster/blob/b2064486470ccb0250a0cb49672cf4575ae1631d/generators/client/templates/src/main/webapp/app/services/auth/_auth.jwt.service.js#L93

Note that storing the token in sessionStorage limits the risk of XSS to the lifetime of the session but it doesn't eliminate it !

Maybe there could be another option: use the usual AngularJS XSS Cookie protection with a server-side session, like we do with session-based authent.

Pros:

  • Solves the security issue
  • Same as we do today for session-based authent, so very easy to implement

Cons:

  • Users will need to do sticky sessions -> we are not totally stateless anymore, and not everyone has a sticky session mechanism (as the gateway is an "edge server", it's normal to use it without a front-end load-balancer, and just have some DNS round-robbin to access the instances)
  • Cookies will be sent with each request, with the HTTP Headers, so there is some overhead

Just an update to say that the http://alexbilbie.com/2014/11/oauth-and-javascript/ article has been pulled. Apparently @alexbilbie no longer stands by some aspect(s) of what he was suggesting in this older article. From what I remember, the article was claiming that using implicit oauth2 flows (or more generally, keeping the token in the browser) was inviting XSS attacks and that a thin server-side proxy should be used. I reached out to him on twitter and he has new thinking on the matter...that he hopes to post about soon. https://twitter.com/alexbilbie/status/743475042273624065. My guess is that the risk of JWT exposure in the client is not as big as originally thought?

I know this a really old issue, but I consider it as non-answered, since pointing out that none of the storage options are (completely) valid for security. And most google searches usually point to this URL, I encourage you to consider and review a splitted token approach.

Hi @Frondor, yes I agree that storing tokens on local/session storage has a vulnerability if your site is compromised by some malicious code in a dependency or from code in CDN, but the entire premise of this being less secure is based on the assumption that there is malicious code running on your domain, it wouldn't work if the code is in an iframe so lets exclude those cases.
So let's consider that someone managed to inject code into your site via CDN or via an NPM dependency. Now here are all the things they could do using that JS code (Please note that I'm not a security expert, this is based on my understanding of what can be done with JS)

  1. Steal data in local/session storage and send it to a server and figure out the tokens from them(somehow) and use that to send a request to the server
  2. Look for username, password fields and capture keystrokes from it and use it to steal your identity(IMO this is much easier and effective then stealing your JWT token)
  3. use your own angular/react services(Bit more difficult but not impossible by clever trial and error) to fetch data and send it to their server without having to steal JWT thus circumventing all your security measure in place

So IMO if there is malicious code injected on your site, then losing JWT tokens are not your only concern, If I can inject code into someone's site within their domain, I can do a lot of harm regardless of what security(JWT, Session, Oauth) you have implemented, so I'm not sure how any other methods suggested will help in this case?

I do agree that not having tokens in storage will reduce the attack surface a teeny weeny bit

@deepu105 Don't forget about browser extensions accessing every website's Web Storage. Your app dependencies may be clean from untrusted code, but your user browser can be compromised. Does it?
If that's the case, I don't think the XSS attacker can access the in-memory js instance of your app. Then, a proper Content Security Policy should do the job.
When your app becomes a XSS victim, I agree that stolen access tokens are just one of your concerns, but still a very important one, I shouldn't be ignored in my opinon.

@Frondor yes I agree, I'm not trying to say that its not a problem, my point is that if you are an XSS victim then you are compromised regardless of how you store your JWT key. Browser extensions can read html forms as well(Lastpass for example)

Btw i'm not sure if extensions can read the local storage of the app due to same origin policy

So my point was that trying to fix only JWT in localstorage doesn't necessarily secure you against XSS attacks. If I'm an attacker my first attempt would be to read login details from forms. The solution is to ensure that you don't open yourself up to XSS attack, this is rather hard problem and only way is educating users.

So fixing this particular issue of JWT alone doesn't solve the issue IMO and just complicates the setup further. Anyway i'm not against changing this if there is a nice solution without much overhead and if everyone else agrees

By the way, we could also use other tricks to make it more difficult to use the tokens even if stolen, one way I could think is

  1. Encrypt the token before storing it on storage
  2. Generate a random decryption key in memory and use that to decrypt the token before sending it server side

This still doesn't solve the issue but it makes it much more difficult to use the tokens without knowing the source code of the application

Still, the injected script can listen for authentication responses (overriding XMLHttpRequest for example) and get the token before the app can even encrypt it.
And my head just exploded :laughing: your example made me think about that method... And now I can't think on a solution for that (intercepted responses). So my question now is, is it possible for an embedded script to listen to your in-memory app XHRequests and get the body contents?
ANYWAY, if that's possible, the approach I linked is still valid and circumvents that, because even intercepting your app responses, he can't access the _other_ part of the _splitted token_ stored in the _httpOnly_ cookie :open_mouth: :boom:

@Frondor yes thats another way to do it, and I guess its possible as well

Was this page helpful?
0 / 5 - 0 ratings