Here’s Why Storing JWT in Local Storage is a Disastrous Mistake

You are probably doing this, so please stop!!

Vikranth Kanumuru
Kanlanc
11 min readJul 31, 2021

--

Photo by Lewis Kang'ethe Ngugi on Unsplash

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 secure storage. This might contradict everything you read so far, so bear with me.

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 in older tutorials, sessions and cookies were kings. This led me to believe that JWT was the next breakthrough or step in cybersecurity and like everyone, I was taught by the many countless articles to store them in local storage.

But after a fated moment of curiosity after wondering “Why is JWT better than cookies and why are they stored in local storage” did I realize how bad of a mistake I was commiting.

One perspective is that these are beginner tutorials and do no harm to anyone but another perspective is that they set up beginners to have the worst foundation possible!

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.

If you run the JavaScript code above in your browser by clicking F12(developer tools) and pasting the code into the console page, you should see the phrase “kanlanc has 99 number of followers and needs your HELP to get to 100 followers” in the console window.

If you then type “localStorage” into your console window, you’ll be able to see that both the userName and followers variables are stored in local storage in your browser.

As you might have guessed already, the reason local storage has been such a hit with developers is because it allows you to cache data that can be used later on, and furthermore the storage space goes up to 5MB! ( While you are limited to a size of 4KB with cookies)

In doing so, you don't have to deal with any backend or external data stores for storing data in the browser. Use cases would be Static sites, SPA(Single Page Applications), and such.

Then what’s so bad about Local Storage?

It isn’t necessarily bad, but just…basic!

Let me explain:

  • Synchronous: This means each local storage operation you run will be one at a time. For complex applications, this is a serious issue as it will slow down your app’s runtime.
  • String Storage Only: It is only capable of storing string data. This makes it pretty useless for any data that’s even slightly more complex than a simple string.
  • Limited Storage: It 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.
  • Incompatible with web workers: It can’t be used by web workers. This means that if you want to build a Progressive Web App(PWA) application that takes advantage of background processing for performance, chrome extensions, and things like that, local storage is not an option at all as web workers do not have access to it.
  • Security Permissions: Any JavaScript code on your page can access local storage: it has no data protection whatsoever. This is the big one for security reasons, especially if any of the third-party libraries you have included in your app are corrupted or have been breached.

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

Even if you believe that a slower app and a little annoyance while developing is fine, security is one of the topmost priorities 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 Twitter or Instagram that information.

Anyone who “follows” you can access it! (See what I did there ;D)

Yeah, exactly! 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.

— Randall Degges

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.

— Randall Degges

Yah! Not the best idea.

The main issue is that you open yourself to cross-site scripting attacks (XSS).

To give you a high-level summary, if an attacker somehow gets the permissions to run JS on your website, they would be able to access the local storage without any extra permissions, and send any info stored there to whatever place of their choosing like their own domain.

In other words, anything sensitive you’ve got in local storage (like a user’s data) can be compromised.

I know your next thoughts would be

Why the h*eck would I allow another person to run JS on my site?!!

This dude thinks I am incompetent!

I assure you that is not the case but even when you think no one else can run Javascript on your website and that you have secured it properly if your website contains any third-party javascript code like CDN links that have sources outside of your domain like:

  • Links to Font awesome icons
  • Links to CSS frameworks like Bootstrap, Bulma, etc.
  • Links to JS frameworks like Jquery, React, etc. (or)
  • Links to traffic tracking sites like Google Analytics

Then you are currently at risk of having an attacker run JavaScript on your website!!

Let’s say your website has the following script tag embedded inside it.

In an unfortunate scenario, if favouritejslibrary.com is compromised by a malicious party and now their minified.js script gets modified to:

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

then you are..… in very simple terms….. completely screwed.

If such an event comes to pass, all your user information would be in the hands of the attacker and the worst part is, you wouldn't even know!!

So, remember two things:

  • Do not use every library you see on the internet
  • Do not store anything even remotely important in local storage

After learning this, you might consider completely avoid using third-party libraries, but unless you want your website that takes years to build, it is not a feasible solution.

So considering the possible scenarios, to reduce the risk of a security accident, try not to store anything important in the local storage.

Now, after all this explaining, I believe you understand this simple thing.

JWT in Local Storage = Major Security Issue

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.

— Randall Degges

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.

Just like how every teacher thinks at least once in their lifetime,

We really need to update our syllabus.

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

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:

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.

— Randall Degges

Most stateless JWTs are > 4kb ( This is quite easy to confirm. Just get a user from your Database into a JWT and take a look at its size. Almost in every case, the resulting JWT exceeds the 4KB limit). Since that's the situation, you are in essence, back to using the 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, you STILL NEED to have centralized sessions that handles revocation, right?.

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.

— Randall Degges

Now, Are Cookies Safe Right Out of The Box?

The answer is No!

As mentioned in several comments by Faiwer Zubashev and Geordy James, using cookies alone is not the solution but extra steps to prevent XSS attack must be taken by enabling “HTTP-only” parameter in cookies which basically do not allow any third party JavaScript code to read your cookies and enabling the secure flag which transports your cookies only through HTTPS.

If you would like to read more about that, here are some good resources

If you would like to learn what is a XSS attack in more detail, find it here

Thank you for reading.

I hope this article helped you to understand:

  1. Why you shouldn't be storing JWT in local storage if you would like to prevent putting your website at risk from malicious users
  2. Alternatives to storing JWT’s in local storage
  3. The benefits of using cookies when compared to JWT’s and
  4. How to configure your cookies to be more robust

I hope you enjoyed reading it as much as I had fun making it!

Want more from me?

  1. Subscribe to get notified when I publish articles.
  2. Support my writing with no extra cost to you by becoming a referred Medium member.
  3. Connect and reach me on Linkedin and Twitter

My Other Popular Articles You Might Like:

References:-

--

--

Vikranth Kanumuru

A Curious Fellow in love with Technology — Featured in ABC Australia| 70K+ Views | 9 x Top Writer in Innovation and Startup — https://portfolio.kanlanc.com