Why JSON web tokens shouldn’t be stored in local storage

JWT VS Cookie Storage analysis and review. Pro's and Con's of both and what to lookout for.

Why JSON web tokens shouldn’t be stored in local storage

Why JSON web tokens shouldn’t be stored in local storage

I’ve always wondered: why are we still using cookies when we can use JSON Web Tokens (JWT)? They offer so many benefits like being stateless, self-storing, and so on.

After digging and asking around, I found out that local storage was never meant to be used as a secure storage. Bear with me here.

I came across many tutorials that tell you to put JWT in local storage — in fact all articles on JWT that I came across said this. But whatever the reason they advise it, this practice needs to stop.

I have seen many sites where sensitive information was stored in local storage. And it bothers me to know that so many developers are opening themselves up to devastating security issues by doing so.

First let’s understand what is local storage?

Local storage is a new feature of HTML5 that basically allows you (a web developer) to store any information you want in your user’s browser using JavaScript. Easy peasy.

In practice, local storage is just a JavaScript object that you can add or remove data. Here’s an example of some JavaScript code that stores some of my personal info in local storage and also removes it.


// Either this
// Considering it as a object
localStorage.userName = "highskillzz";
//or this way!
localStorage.setItem("objects", "0");

// Once data is in localStorage, it'll stay there forever until it // is removed explicitly 
console.log(localStorage.userName + " has " + localStorage.objects + " number of objects.");

// Removing data from local storage is also pretty easy. Uncomment 
// below lines
//localStorage.removeItem("userName");
//localStorage.removeItem("objects");

If you run the JavaScript code above in your browser on a test HTML page, you’ll see the phrase “highskillzz has 0 number of objects” in a console window. If you then open up your developer tools, you’ll be able to see that both the userName and objects variables are stored in local storage in your browser.

json-1


What’s Awesome About Local Storage?

Although this article is about why not to use Local Storage, there are many cool things about it too. Like….
It’s a pure JavaScript object. If you’re building a single page site, using something like local storage means your web pages can run independently of any web server. You need only the browser storage and there would be no need for storing any data in the server.
Also, the other great thing about local storage is the size restriction: we can use it to cache data for later usage. But in the case of cookies, this isn’t the case as there is a size restriction of 4KB, while Local storage provides at least 5MB of data storage across all major web browsers.


Then what’s so bad about Local Storage?

I feel like many developers might not realize just how basic local storage actually is:

  • It can only store string data. This makes it pretty useless for storing data that’s even slightly more complex than a simple string.

  • It is synchronous. This means each local storage operation you run will be one-at-a-time. For complex applications this is a big no-no as it’ll slow down your app’s runtime.

  • It can’t be used by web workers. This means that if you want to build an application that takes advantage of background processing for performance, chrome extensions, things like that: you can’t use local storage at all since it isn’t available to the web workers.

  • It still limits the size of data you can store (~5MB across all major browsers). This is a fairly low limit for people building apps that are data intensive or need to function offline.

  • Any JavaScript code on your page can access local storage: it has no data protection whatsoever. This is the big one for security reasons. That is pretty bad.

In a nutshell, the only situation in which you should use local storage is when you need to store some publicly available information that is not at all sensitive, doesn’t need to be used in a high-performance app, isn’t larger than 5MB, and consists of purely string data.

Why Local Storage is Insecure and You Shouldn’t Use it to Store Sensitive Data


Although even if you consider that a slower app and a little annoyance while developing is fine, Security is the top most priority and cannot be cast aside. The security model of local storage is really important to know and understand since it will dramatically affect your website in ways you may not realize.
Storing in local storage any sensitive information is equivalent to posting on facebook or instagram that information. Yeah! it’s pretty bad. Local storage wasn’t designed to be used as a secure storage mechanism in a browser. It was designed to be a simple string only key/value store that developers could use to build slightly more complex single page apps. That’s it.

Think about it like this: when you store sensitive information in local storage, you’re essentially using the most dangerous thing in the world(javascript) to store your most sensitive information in the worst vault ever created, yes definitely, not the best idea.

What the problem really boils down to is cross-site scripting attacks (XSS). I won’t bore you with a full explanation of XSS, but here’s the high level:
If an attacker can run JavaScript on your website, they can retrieve all the data you’ve stored in local storage and send it off to their own domain. This means anything sensitive you’ve got in local storage (like a user’s session data) can be compromised.

But you might believe that Javascipt cannot run in your site and you have secured it properly, but if your website contains any third party JavaScript code included from a source outside your domain like links to cdn like:

  • Links to bootstrap
  • Links to jQuery
  • Links to Vue, React, Angular, etc.

or

  • Links to any ad network code
  • Links to Google Analytics
  • Links to any tracking code

Then you are currently at risk for having an attacker run JavaScript on your website. Let’s say your website has the following script tag embedded inside it.

<script src="https://awesomejslibrary.com/minified.js"></script>

In this case, if awesomejslibrary.com is compromised and their minified.js script gets altered to:

  • Loop through all data in local storage
  • Send it to an API built to collect stolen information

... then you are completely screwed. In this situation the attacker would have easily been able to compromise anything you had stored in local storage and you would never notice. Not ideal.
You might consider not at all using third partly libraries but as we all know that is not at all possible if you want a good modern site working.
So to err on the side of caution and dramatically reduce your risk for a security incident: don’t store anything sensitive in local storage.

The biggest security offenders I see today are those of us who store JWTs (session data) in local storage. Many people don’t realize that JWTs are essentially the same thing as a username/password.

If an attacker can get a copy of your JWT, they can make requests to the website on your behalf and you will never know. Treat your JWTs like you would a credit card number or password: don’t ever store them in local storage.

There are a lot of tutorials, YouTube videos, and even programming classes at universities and coding boot camps incorrectly teaching new developers to store JWTs in local storage as an authentication mechanism. This is not entirely correct and has a lot of flaws with it.

What is the alternative to Local Storage then???


Well there are two possible alternatives:-

  1. Instead of storing the JWT in local storage, store it in a cookie(I don’t recommend this. Read on to find out why)
  2. The other is to use server side authentication by using sessions and cookies(Recommended)

There are alternatives to using JWT altogether:-

Read more about this here - https://kev.inburke.com/kevin/things-to-use-instead-of-jwt/

Although this article is for showing that JWT in local storage is not good, there are reasons why you shouldn’t be using JWT altogether, that can be read more about here:-

Why JWTs Suck as Session Tokens
JSON Web Tokens (JWTs) are so hot right now. They’re all the rage in web development: Trendy? ✓ Secure? ✓
Stop using JWT for sessions - joepie91′s Ramblings
Pros and cons in using JWT (JSON Web Tokens)
For apps that require a sever-side implementation, the clients (mobile app or web browser)generally have to prove their identity to the server. i.e. a user using Chrome to open facebook.com that he…

The second option is recommended and tutorials can be found almost anywhere so I won’t be explaining why but instead why not the first option.

Storing a JWT in the cookies is perfectly OK and it has the advantage of not needing custom JS code to pass it to each HTTP request to your backend.
But in some situations, like when your API is also used by your mobile app and it requires the “Authorization Bearer xxx” header instead of a cookie or when you’re making HTTP requests to multiple backends but with same JWT, it’s convenient to have your JWT in localStorage instead.

This is correct but 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.

Finally, using session cookies is not only faster/more secure, but far simpler and safer for 99% of developers to use. If you’re in the 1% who knows what you’re doing and is willing to make the tradeoff, go for it. But for 99% of people out there, it’s a bad idea.

Thank you for reading. I mostly write tutorials on the problems I have faced and whatever new tech I explore, so stay tuned for the next article.

If you found this article helpful, don't forget to subscribe for more 👏.

Note:-

A lot of people mistakenly try to compare “cookies vs. JWT”. This comparison makes no sense at all, and it’s comparing apples to oranges — cookies are a storage mechanism, whereas JWT tokens are cryptographically signed tokens.
They aren’t opposites — rather, they can be used either together or independently. The correct comparisons are “sessions vs. JWT” and “cookies vs. Local Storage”.

References:-

Why using localStorage directly is a bad idea - Michal Zalecki
In my perfect bubble localStorage always works and I can rely on it unless you are using Opera Mini. The reality is a little bit different.
Randall Degges - Please Stop Using Local Storage
Stop using local storage to store sensitive information. If you’re putting a JWT in local storage you’re doing it wrong.
Is localStorage bad for performance?
Mozilla evangelist Christian Heilmann slams localStorage and says that fragmentation is inevitable
Discussion of Please Stop Using Local Storage
Local storage shouldn’t be used to store sensitive data. It’s unsafe!