Skip to content
loading...

re: Please Stop Using Local Storage VIEW POST

TOP OF THREAD FULL DISCUSSION
re: While some of your points are correct, suggesting to use sessions and cookies instead of JWT is missing the point. People using JWTs are most proba...
 

Hey, this is a great point. But hear me out.

Let's say you want to store a JWT in a cookie -- that's fine. BUT: the purpose of JWTs is to be stateless, right? Cookies are capped out at 4k, which means the JWT needs to be < 4k for this to work.

Most stateless JWTs are > 4kb (this is quite easy to test, serialize a user from your DB into a compact'd JWT and look at the byte size -- I've done this on a lot of sample apps and in almost every case except the most simple the JWT is > 4kb). In this scenario, you've basically got to use local storage.

Instead of doing that: why not just use a session cookie as I recommended? The downside is that you need to manage a cache on the API side, but this is easily doable. If you're using JWTs anyway (with local storage, let's say), you STILL NEED to have centralized sessions in some way to manage revocation.

JWTs are insecure by design: they cache authentication/authorization data, so in order to work around their speed-vs-security tradeoff you've got to manage a revocation list centrally no matter what: otherwise you end up in situations where revoked permissions/data are being allowed through -- a poor scenario.

Now, let's talk about your point about using the Authorization header. I hear this a lot: I think I will write another article about it as it's a common misconception.

There is NO DIFFERENCE between sending auth data to a server in the Authorization header (with a bearer token) vs sending it to the server using a cookie. COokies are just HTTP headers as well. The difference is a few characters:

Authorization: Bearer <token>
vs
Cookie: session=<signed(id)>

Any server can parse either header. While you may not want to for some reason, there is nothing technical or bad about using a cookie for this purpose.

I'd argue that the ONLY reason you need to use a JWT for auth is if you're using OpenID Connect, as for some reason the authors of the spec decided to standardize on JWTs for the ID token. In this case, using the JWT and putting it into an Authorization is actually required -- outside this case? Not at all.

And re: scripts being minified/included... This does not apply to the biggest use case I mentioned in the article: third party tools. Things like GA, Optimizely, Kissmetrics, and all the other marketing/ad network tools that most companies use.

There are definitely scenarios in which you can make yourself safe from XSS, but those situations are increasingly rare.

As always, this article is only meant to provide guidance to the 99% of people who do this stuff blindly without thinking about it. In your case, you likely know the risk profile for what you're doing and can decide whether or not you want to take the risk.

 

I don't think you understand that APIs don't scale with sessions maintained on server side. That's why we no longer use session cookies. Storing JWT in http only cookies is not perfect, but ok from scalability perspective.

 

Content-security-policy for XSS + subresource integrity hashes for third party hosted libraries, or just compile in the third party tool like google analytics, e.g. npmjs.com/package/analytics

There are many improvements being made to the security of single page applications (HSTS en.wikipedia.org/wiki/HTTP_Strict_..., certificate transparency en.wikipedia.org/wiki/Certificate_...) that make storing sensitive information in JS about as safe as storing them in a mobile app.

 

JWTs aren't inherently unsafe. I like that JWTs are signed by a server side secret, and I like the flow that creates during auth. Depending on how secure the app needs to be, I've even stored user agent and remote address info in a JWT, signing it with a user specific secret. I had the JWT checked for all of that as accurate against headers and then refreshed the sign/verify secret if the JWT had data that was bad in it.

Most apps can get away with an expiry on a JWT and increase performance, this decreasing energy usage, this decreasing environmental impact of your code.

The CIA of information (Confidentiality, Integrity, Availability) is held strong. OP only focuses on Confidentiality for some reason.

 

you've got to manage a revocation list centrally

This might be true for Google or Amazon but is not actually true for the majority of applications out there I would argue.

You find out a token is compromised? Just regenerate your signing key. Yes every application user will have to log in again but that is a perfectly acceptable action for most applications.

 

One problem with the article is that the words 'never' and 'always' are used way too often.

Also, the article says that localStorage is insecure quite often but doesn't give any evidence or examples.

I would argue that localStorage is as secure as cookies (including httpOnly cookies).

localStorage uses essentially the same security policy as cookies; one of its core principles is that a domain cannot access localStorage data that was created under a different domain so there is no chance that a website could steal data from a different website.

Also, httpOnly cookies do not make your site any less vulnerable to XSS attacks; if the attacker manages to inject a malicious script into your front end, then they can use that script to make HTTP requests to your server (directly from the victim's browser) and your precious httpOnly cookie (containing the user's valid session ID) will be attached to every request so the server will service them without suspecting anything.

The only real difference is that if the token (e.g. JWT) is in localStorage then the attacker can steal the token to use later (same goes for regular non-httpOnly cookies BTW)... Which is hardly a convenience because it's more advantageous for the attacker to carry-out the attack in-place from the victim's browser rather than from the attacker's own machine (thus allowing their IP to be traced directly).

Also, with JWTs, it's good practice to set short expiry dates. If you're using WebSockets you can even issue JWTs with 10 minute expiry (for example) and re-issue a new one automatically every 8 minutes if the user is still connected and logged in; then when the user logs out or becomes disconnected; their last issued JWT will become invalid in only 10 minutes (at which point it becomes completely useless to an attacker).

Also, it's not recommended practice to store large amounts of data inside a JWT because of the overhead of having to send it with every request/connection.

woow this is one of the best comments in this post.

Indeed the original article totally misses the point that local storage is by no means less secure than any other part of your website. If you have an XSS, you are flawed. That's actually the reason why XSS attacks are so serious/bad.

If you fetch from the browser, you don't get cookies. You have to add {credentials: "include"}. And that requires a whitelisting on the server. So no, it's no so easy to get httpOnly cookie content in browser as you describe. It requires a TRACE method or other known vulnerability or bug to expose them. Pls. prove me wrong if you think otherwise.

Other than that, I agree with your point. When an app has an XSS injection there are tons of attack vectors a hacker can take. And yes, localStorage is almost the same as cookies security-wise. This article is mostly a FUD, unfortunately.

His point was that you don't need to read the cookie if you can send requests when the user is on the compromised website.

code of conduct - report abuse