DEV Community

Belhassen Chelbi
Belhassen Chelbi

Posted on

The internet is wrong about JWT

It's the bit where you use localStorage to store it. It's vulnerable to XSS yet there are a lot of articles out there again and again using localStorage.

The problem is, not just articles, but packages like django-rest-simplejwt and solutions like saleor are using it this way (for the record, Saleor has this as a high priority issue for the next release). It's not only these, the default behavior for almost every framework out there is the same.

Shouldn't the default behavior be an Http-only cookie returned rather than a json object containing the access and refresh token? Because if it's not secure, why do I even need it as plain text?

What do you think?

Top comments (25)

Collapse
 
dividedbynil profile image
Kane Ong • Edited

Here is my opinion:

Storing JWT in localStorage and building a SPA with 3rd party JS libraries is not a sin, as long as you know how to handle the risk. Don't panic.

Let's look at it from different perspectives.

From technical standpoints

Using JWT as a stateless authentication for backend servers can minimize backend architecture complexity and workload significantly.

Working on top of 3rd party libraries improve developers' efficiency, developers are able to spend less time to finish more tasks on hands.

If you choose to follow the security firms' (like OWASP) recommendation dogmatically, then you will spend more time and resources to build a simple app and maintain it. The pragmatic developers should learn how to deal with the risk without giving up many benefits one could have in the eco-system.

From business standpoints

If you have JWT in use for years in a complex enough application, it is impractical to rewrite the whole authentication mechanism to avoid the risk. Pragmatically, you should deal with the risk instead of totally avoid it, as many big software firms nowadays set up bug bounty to attract security researchers rather than hire security auditors to flag the possible risks.

Keep in mind that how JWT minimize your server cost and improve your productivity, giving it up may cost you your business.

How to deal with it

The idea is simple, don't trust any user input is another way to prevent XSS (malicious JS injection), so sanitizing every user input (front-end and back-end) can avoid it from happening.

As for 3rd party libraries, you should find a way to evaluate the risk rather than totally avoid it.

By having the precautions above, storing a JWT in localStorage is totally acceptable.

Collapse
 
belhassen07 profile image
Belhassen Chelbi

Thanks for your well thought reply. My point here is not to drop the use of JWT but to use it in the most secure way possible. JWT is not insecure by design but the use of it in almost every application is wrong. At this point in time Http only cookie are the better choice, it has its own risks but compared XSS, it's a much safer choice.

Collapse
 
dividedbynil profile image
Kane Ong • Edited

Storing JWT in JS inaccessible place is pointless, if you choose to store JWT in HttpOnly cookie, you cannot access the JWT payload at the front-end.

How do you come to the conclusion that "JWT is not insecure by design but the use of it in almost every application is wrong."? How wrong is that? In which context?

"At this point in time Http only cookie are the better choice, it has its own risks but compared XSS, it's a much safer choice." In which context HttpOnly cookies are the better choice? By security standpoint only? What if XSS risk has resolved, does it matter anymore?

Thread Thread
 
belhassen07 profile image
Belhassen Chelbi

First of all, I apologize, I should've been more precise.

First of there are 2 types of tokens: The refresh and the access token.

The refresh token is the one that gets stored inside an http only cookie.

The access token gets returned to the client and gets stored in memory (application state) no localStorage.

With a silent countdown, the client refreshes the token by hitting the refresh endpoint (http only cookie gets sent automatically by the browser)

Long story short, from the hasura.io blog

"Persisting JWT token in localstorage (prone to XSS) < Persisting JWT token in an HttpOnly cookie (prone to CSRF, a little bit better for XSS) < Persisting refresh token in an HttpOnly cookie (safe from CSRF, a little bit better for XSS)."

Please note a lot of OSS are going in this direction as it seems by the industry experts, the safest way for JWT.

Thread Thread
 
dividedbynil profile image
Kane Ong • Edited

Thanks for the details, just as I said, storing JWT in an HttpOnly cookie is gilding the lily since it contradicts with the purpose it serves, JWT is not only an access token but also an authentication info carrier(non-sensitive info). Now I know that you only regard JWT as another access token, so you think it's wrong to store it in a relatively easy place to access (by any malicious JS injection). In fact, one of the reasons to store it in an accessible place is to cater to the convenience of extracting the JWT payload info anywhere in the front-end applications. So don't overlook the purpose of JWT before making claims.

"Please note a lot of OSS are going in this direction as it seems by the industry experts, the safest way for JWT."
Safest, are you sure? Moreover, do you imply that follow this direction is the safest bet for every project in the entire industry? Since you're wrong (incomplete understanding, to be more precise) with the concept of JWT I regard your "safest way of JWT" as the "safest way of access token".

"The access token gets returned to the client and gets stored in memory (application state) no localStorage."
Not using the localStorage to store access token does not make the access token invulnerable from XSS either.

Thread Thread
 
belhassen07 profile image
Belhassen Chelbi • Edited

The refresh token is the one that gets stored in the http only cookie and no, if you're storing it in the application memory, it's not vulnerable to XSS. And please oh please, stop going on offense. It's not my safest way. And buddy, I don't think someone who's researching about the security of JWTs, doesn't even understand the concept itself.

Thread Thread
 
dividedbynil profile image
Kane Ong • Edited

Please don't take it as an offense, I'm only sharing my opinions, you can ignore it totally. As this is a discussion post, I also don't regard your post and reply as offenses.

If you only look for winning the argument, I will stop here. It is meaningless to me as well. I am not an evangelist for JWT.

Thread Thread
 
belhassen07 profile image
Belhassen Chelbi

No worries buddy. Other than JWT, do you use other methods for authentication when making a decoupled frontend and backend?

Thread Thread
 
dividedbynil profile image
Kane Ong

I am glad you didn't take it personally. Nice question, other than JWT, I prefer token-based authentication.

Thread Thread
 
dividedbynil profile image
Kane Ong

@belhassen07 just a kind reminder, you're very welcome to bring/invite the experts you know to join this thread for discussion as well if you feel uncomfortable with my replies.

Collapse
 
dividedbynil profile image
Kane Ong • Edited

May I have the reference for your claim on the localStorage XSS vulnerability?

Collapse
 
matthieume profile image
Matthieu Barthelemy

You can find most information about the JWT storage issue on this cheatsheet from OWASP: cheatsheetseries.owasp.org/cheatsh...
The whole page is describing guidelines about JWT usages.

If you want to know more about JWT vulnerabilities there is this github with many attacks: github.com/ticarpi/jwt_tool/wiki

Collapse
 
dividedbynil profile image
Kane Ong • Edited

Okay, let me be clear and specific, kindly show me the deal-breaker why we should choose cookie over JWT in localStorage. Is the token in localStorage more vulnerable than cookie?

Thread Thread
 
belhassen07 profile image
Belhassen Chelbi

"Storing tokens in browser local storage provides persistence across page refreshes and browser tabs, however if an attacker can achieve running JavaScript in the SPA using a cross-site scripting (XSS) attack, they can retrieve the tokens stored in local storage. A vulnerability leading to a successful XSS attack can be either in the SPA source code or in any third-party JavaScript code (such as bootstrap, jQuery, or Google Analytics) included in the SPA."
Auth 0 Token Storage

Thread Thread
 
twigman08 profile image
Chad Smith

Honestly that's one of the issues I have.
Auth0 talks about those issues and still says that it is viable way to store the token

That's one of the reasons I actually haven't gone with Auth0 for some of my identity needs. Their documentation contradicts other places in their documentation.

Thread Thread
 
dividedbynil profile image
Kane Ong • Edited

Great point indeed. But the assumption is based on the untrusted JS code. If that's the case, a HttpOnly cookie is not the rescue either, due to all the 3rd party JS code technically could steal all input data of users, including data from the user name and password field. From that assumption do you think HttpOnly is safer?

Thread Thread
 
matthieume profile image
Matthieu Barthelemy

Localstorage is vulnerable to xss attacks (you can read it using JavaScript). HttpOnly cookies are by design protected (you can't read it from JavaScript).
OWASP recommendation is to never store JWT token in the localstorage. You will get the details on their website.

Thread Thread
 
dividedbynil profile image
Kane Ong • Edited

I know, thanks for clarifying, the assumption is based on malicious JS code injection, if that's the case, HttpOnly cookies are not the rescue either, as the injection can focus on stealing password rather than cookies.

Thread Thread
 
matthieume profile image
Matthieu Barthelemy

From a pragmatic point of view: it's all about the surface attack : it should be almost impossible to xss a login pages, as no dynamic contents should be inside of it (or SSO uses can also remove the risk).
For the rest of the application, we all knows that the attack surface is bigger: user information displayed, user comments, contents, file uploads...

Anyway, the "do not use the localstorage" is the OWASP recommendation. You are free to not follow it. But any security auditor will flag this behavior.

Thread Thread
 
dividedbynil profile image
Kane Ong • Edited

Great point of view, so the login pages should not have any third-party JS library either, right? And a SPA should also have separate pages (no 3rd party library, no dynamic content) for password input (login/sign up/changing password) to reduce the surface?

Alright, then by reducing all the attack surface, does it matter to use a cookie with HttpOnly, or JWT in localStorage?

Thread Thread
 
belhassen07 profile image
Belhassen Chelbi

As for the Auth0 documentation, yes they do have a contradiction in what they say especially when they were recommending using localStorage but that's an older article.

Collapse
 
dtinth profile image
Thai Pangsakulyanont

It is the security-convenience tradeoff.

For example, Firebase Authentication, by default, stores your tokens in local storage (actually, it stores in IndexedDB, but if an XSS can access localStorage, it can also access IndexedDB). Does it mean that Firebase Authentication is “insecure by default” and “wrong”? Does it imply that people should never use Firebase because it handles authentication in a way that a token can be leaked via an XSS?

I saw so many articles across the web about how it is “wrong” to store JWT in client-side storage. Yet to this day, I did not see anyone complain about it on firebase/firebase-js-sdk issue tracker.

I’d echo @dividedbynil ’s opinion — I think storing tokens in client-side storage is fine if developers know how to handle the risk.

Collapse
 
belhassen07 profile image
Belhassen Chelbi

"if developers know how to handle the risk." . IMHO, I don't think our perception of our skills can be an excuse or a pass for us to ignore security measures recommended by industry experts.

As for firebase, I would need more time to investigate that claim.

Collapse
 
dtinth profile image
Thai Pangsakulyanont • Edited

IMHO, I don't think our perception of our skills can be an excuse or a pass for us to ignore security measures recommended by industry experts.

This sounds like textbook argument from authority to me…

I don’t think that security measure is ignored. It’s heeding the warning, but after taking context into consideration, decide that it is irrelevant, or not worth the effort.

For example, there are client-side libraries that tries to protect from XSS by default. As well as browser’s security features such as content-security-policy and XSS protection. If we use that and do not do anything dangerous, that the arguments about XSS becomes less risky.

If a JWT is short-lived, it will expire, limiting the impact when a JWT is compromised. (Refresh tokens, on the other hand, become a more serious issue if compromised, and may need more security measures.) The thing is, context matters.

Then there are economics factor. Implementing server-side authentication may cost more (in terms of development effort) than just storing a JWT client-side. If I’m building a simple web-based client-side game with a leaderboard (no sensitive information is stored) using a secure-by-default library, then the benefits doesn’t outweigh the costs.

Collapse
 
codesinthedark profile image
Srdjan Mitrovic • Edited

You cannot have refresh token in a cookie if you use 3rd party API (firebase) to get new access token, because nowadays third party cookies are disallowed.
The solution is that you can use refresh token only once and after that it is invalidated and you get new refresh token. Old one will not work anymore. However, if you try to use refresh token and see that it is invalid then a breach is detected and you can login again and invalidate other compromised refresh token and get new valid one.