DEV Community

Please Stop Using Local Storage

Randall Degges on January 30, 2018

Seriously. Just stop it already. I don't know what it is, exactly, that drives so many developers to store session information in local storage,...
Collapse
 
paztek profile image
Matthieu Balmes

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 probably running a SPA and communicating with a pure stateless API. We don’t want to hear about sessions in this case.

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.

Also, most modern JS frameworks (React, Angular, ...) nowadays produce concatenated and minified vendor scripts so you don’t have to include single scripts from third party locations.

Collapse
 
rdegges profile image
Randall Degges

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.

Collapse
 
jondubois profile image
Jonathan Gros-Dubois • Edited

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.

Thread Thread
 
ilusionlinux profile image
Luis Morales • Edited

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

Thread Thread
 
rugk profile image
rugk

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.

Thread Thread
 
ivankleshnin profile image
Ivan Kleshnin • Edited

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. This article is mostly a FUD, unfortunately.

Thread Thread
 
maciek134 profile image
Maciej Sopyło

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.

Collapse
 
togakangaroo profile image
George Mauer

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.

Collapse
 
inf3rno profile image
inf3rno

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.

Thread Thread
 
branislavlazic profile image
Branislav Lazic

First of all, you’ll need hundreds, if not even thousands of requests per second until your API stops scaling. Second thing, sessions can be serialized and stored in an external data store such as Redis or even Postgres. This way, you may easily scale up or down your API since you moved session state outside of your service. Yes, you will need to take a look at that data store each time when you want to check session validity, but this is very fast. The issue is that session data store becomes a single point of failure, but so is modern authentication service such as Keycloak. If Keycloak goes down, users will start to log out as soon as their JWT’s start expiring since it will be unable to issue new JWT’s. Third and final thing, a common sense. As I already wrote, it will take hundreds or thousands of requests per second before you need to scale to additional instance. In my experience, an app which handles 200-500 requests generates so much money that you can afford to rewrite your authentication layer from scratch implementing your fancy stateless tokens.

Collapse
 
dkamer profile image
David Joseph Kamer

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.

Thread Thread
 
branislavlazic profile image
Branislav Lazic • Edited

JWT is inherently unsafe for the sole purpose it cannot be immediately invalidated. The only way to force invalidation is to change a signing key. If you’re blacklisting JWT’s, then you’re using it wrong. Blacklisting requires preserving a state and checking against it. If you’re saving them, then you’re using them wrong (there’s no a plus in scalability over sessions). If you’re using refresh tokens, then again, you have a state. Plus, it leads to number of bad practices, such as storing them in local storage which can be exposed to XSS attacks, use of long lived JWT’s, or storing sensitive informations in their payload. What would be the correct way to use them? Probably by having a dedicated authentication server like Keycloak which can use short lived JWT’s in combination with refresh tokens and key rotation. But, do you really need such a complicated architecture in the end of the day?

Collapse
 
moodysalem profile image
Moody Salem

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.

Collapse
 
mindlace profile image
Ethan Fremen • Edited

I think you’re misreading him. He doesn’t say “don’t use JWT” he’s just saying “don’t stick your JWT in local storage”.

Yes, it’s convenient. It’s also convenient to just leave the key to my house in the lock, but it means anyone who can get to the door can get in.

Collapse
 
mighub profile image
Mig

JWTs are insecure by design

Considering JWT are most likely used for security, it is pretty much urging not to use them. Unless you need a token for accessing public data.

Many people don't realize that JWTs are essentially the same thing as a username/password.

This one is a stretch and if you go this way, then that is true for pretty much everything used for security nowadays. It is true for cookies. It is true for SSH keys, etc. Any written access that replaces your password can be exploited in a way or another.

Your example with keys is interesting because even if it is not bulletproof, then you are better off typing your password on each request. Except it's not practical.

Don't get me wrong, the article is interesting and everybody should understand the danger of all these practices. But then people will be waiting for the alternative. And mentioning an alternative is the worst thing to do because people will prove you wrong, because nothing is unbreakable.

You're better off warning and help them make sure they are making their best to secure the way they use the current tool.

Collapse
 
bhaibel profile image
Betsy Haibel

Here's a question: in what contexts do SPAs need to use bearer token auth, and is this truly an ideal?

There's only one class of SPAs which CAN'T use cookie auth -- namely, SPAs using a statically served application shell. This is an architectural decision with a lot of tradeoffs to it. On the plus side S3 is cheap, there's a certain theoretical purity to having your web frontend go #serverless, and you only need to maintain one form of API authentication. On the minus side, you have to greenspin literally everything that the browser gives you for free.... like the nice security properties of cookie auth.

There's nothing wrong with maintaining a simple, stupid edge service that exists to maintain sessions & proxy requests to your API. It feels pretty oldschool, but it works pretty well. And if (when) your web frontend winds up wanting a sliiiiightly different API than your mobile app, having an edge service already in place makes that trivial! Most existing SSR frameworks (Next, Nuxt...) can serve this purpose without too much modification of an existing app-shell-style SPA.

Collapse
 
elasticrash profile image
Stefanos Kouroupis

You can work around statically served apps and authentication if you use a concept of an API gateway like Kong. I my case statically served apps are only allowed to go through a Kong like service...in which we can control authentication, rate limiting etc

Collapse
 
grimaud profile image
Pierre

There is another class of SPAs that cannot use cookies: Those that are supposed to work inside an iframe. Third party cookies are denied in any third party http request in Safari already and Chrome is coming. Firefox did the right thing and still allow them while preventing unwanted tracking.

Collapse
 
daniellittledev profile image
Daniel Little

Another great resource to look at is the BFF pattern, worth looking into to explore the idea in this post further.

docs.duendesoftware.com/identityse...
youtube.com/watch?v=UBFx3MSu1Rc

Collapse
 
crazy4groovy profile image
crazy4groovy

Good article and good awareness. However....

Quote: "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."

Edit: "If an attacker can run JavaScript on your website, you're ka-pooched, and a local jwt is the least of your worries."

Collapse
 
kwabenberko profile image
Kwabena Bio Berko

😂😂😂

Collapse
 
pzelnip profile image
Adam Parkin

"If an attacker can run JavaScript on your website, you're ka-pooched, and a local jwt is the least of your worries."

Have you ever used jQuery or any other 3rd party JS library on your site? How certain are you that that 3rd party does nothing malicious? How certain are you that 3rd party library hasn't been compromised?

Not even the library: if you're using one, you're probably pulling from a CDN right? How certain are you that CDN hasn't been compromised to serve a different version of the library than what you think you're getting?

Collapse
 
crazy4groovy profile image
crazy4groovy • Edited

"How certain are you that that 3rd party does nothing malicious?"

I'm not very certain. But you're missing the point. If the library is tainted, what can it do? Much, much worse than the worst case scenario here. That's the point. It's like duct tape to fix the titanic. It's like humans in a Marvel movie. Useless.

Just like Xavier, a compromised lib can mind-control the poop out of your app and wreak havoc for your users, and there's nothing you can do about it. XSS is a security rabbit hole.

When you understand what a tainted lib can do, you realize how trivial this article's point is. That is my only point.

Part of the problem with the proposed solution in this article is that cookies are tied to a specific domain. That makes it much harder and clunky to use third party security services like Auth0. Also, scalability does become a MUCH harder issue with server-side sessions; I don't care what this article says.

Best idea to overcome your tainted paranoia is to use script's integrity attribute, eg:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.slim.min.js" integrity="sha256-pasqAKBDmFT4eHoN2ndd6lN370kFiGUFyTiUHWhU7k8=" crossorigin="anonymous"></script>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
pedro profile image
Pedro M. M. • Edited

Good article but I disagree.

IMHO storing JWT in localstorage is ok. I mean, what is actually not ok given your examples is to be vulnerable to XSS attacks, or using third party scripts/infrastructure you don't trust, in both cases the local stored jwt is the least of your worries –as crazy4groovy points out–.

Here's the thing, we need to store something on client side so they don't have to type the user/password each time we're making a request to the server and yeah EVERYTHING on client side is meant to be insecure. But, does it mean that, for example, browsers shouldn't let users save user/password because third party extensions, etc. can access to them? Hell no.

Yes, if you have the jwt you can access, but is it that bad? That's why they're called bearer tokens: you have one, you can access then. It's like a check payable to cash: they are payable to anyone who presents them. Does it mean that the bank should hire a bodyguard for you? Probably not.

Okeey! I digress, but the point is we need a balance between security and ease. No way I'm using cryptographically signed cookies for storing just a user token for a social network. However, if I were a bank website I would do exactly this.

On the other hand, I agree with the sensitive data point. There is no need for storing sensitive data like addresses or credit cards into the localStorage.

Sorry for the long talk, too much text for a non native speaker like me:)

Collapse
 
rdegges profile image
Randall Degges

Hey! Thanks for the comment.

Storing a JWT in local storage is fine -- the point of this article is to explain to the 99% of developers who do this without thinking about it why it is bad and not recommended by OWASP and security people.

Obviously, if you are aware of the risks and choose to do so anyway, that's fine.

The tradeoff of using a JWT really comes down to this: speed vs security. JWTs are basically opting for speed (and a very minimal amount of speed, I might add) over security.

JWTs were not designed to be used for authentication or authorization data. They were designed to be used as one-time, short-expiration tokens to pass signed data from A->B. Using them as a way to "securely" store session data is abusing them in ways that are directly contrary to a secure mindset.

I guess if I could summarize my thoughts about your comment, I'd say this:

IMO, 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.

Collapse
 
jondubois profile image
Jonathan Gros-Dubois • Edited

Maybe it wasn't the intention but I feel that this article is contributing to hysteria around JWTs/localStorage.

I do software consulting related to an open source project I created and now I feel that I keep having to explain to my clients/companies over and over again why using a cookie would not make things more secure in their case.

I've even been asked to re-implement a perfectly secure authentication system because a security firm that my client was using recommended that they should stop storing their JWTs inside localStorage and that they should store it in the app memory as a global variable instead (as if that was any safer from XSS) - Also the JWTs had a 10 minute expiry so quite safe.

The problem is now that a lot of security consultancies are full of people who like to use articles like this to make blanket decisions on behalf of their clients in order to save them the ordeal of actually having to think about each company's case on an individual basis.

Thread Thread
 
stackcrash profile image
Derek

I just want to jump in real quick as one of those people who like to use articles like this to make blanket decisions. A few points as why security people say not to store session data in JWT and LocalStorage. Out of the box yes LocalStorage is more secure than a cookie for session data, however with the optional flag SameSite cookies are now equal to LocalStorage with built-in anti CSRF protections from the browser. Adding the HTTPOnly flag bring cookies to a higher level than LocalStorage. This is because now client-side JavaScript cannot access the cookies. The last part is where the recommendation to use cookies over LocalStorage is made in relation to potential exposure from XSS. Outside of XSS or vulnerabilities in the browser itself both are limited to exposure from physical access on the client-side in a general risk perspective.

The biggest risk for both options from a security perspective is XSS and while some may be confident in their ability to not introduce XSS, I can say that even the most experienced developers can have a hard time preventing XSS. I won't dive into too specific details but a shockingly common bypass to a lot of anti-XSS solutions is to switch the method from GET to POST or POST to GET in a request. Some popular frameworks even allow submitting GET parameters in the body which is completely wrong from the RFC point of view. The pervasiveness of XSS is why security people make the recommendation, we like to think in layers so if XSS is somehow introduced into the application you still have the layer from HTTPOnly on the cookie verse no additional layer on the LocalStorage side.

TLDR: LocalStorage out of the box is less insecure, but cookies offer more security than LocalStorage when done right. Security people aren't blindly making the recommendation there is a reason.

Thread Thread
 
jondubois profile image
Jonathan Gros-Dubois • Edited

I would argue that using an httpOnly cookie doesn't add any security. At best you could say that it might make it slightly less convenient for an attacker to carry out the XSS attack.

I wrote a more detailed technical explanation here: dev.to/jondubois/comment/373l

Thread Thread
 
bdruth profile image
Brice Ruth

Concur. There's no threat modeling that I could think of that would hold up httpOnly as being a significant factor if the threat vector up to that point has already leveraged XSS - so your local JS context is already 0wned - at this point, the exploit code just needs to directly execute from the compromised browser instead of sending the auth token to a remote server to be exploited from there. Considering the local context is already compromised, that hardly seems more than an inconvenience to the attacker, as jondubois indicated.

Collapse
 
erebos-manannan profile image
Erebos Manannán

Yep, it seems to me that people started working with microframeworks on nodejs etc. thinking they understood what they were doing, then realized they're missing all the convenient features they used to have and didn't have a clue of how they were implemented so they just picked some things that sounded cool (JWT & localStorage) and went with those without even a single second of thought as to "why isn't everyone doing this?" or similar.

Secure information, such as session information, should almost universally be stored in httpOnly & secure cookies, potentially with SameSite=strict. I just don't see any reason for JS to touch the cookies in general, just use a /me endpoint or similar to check if you're logged in and to fetch your user's information so you don't need to touch the secure cookies.

Collapse
 
kpollich profile image
Kyle Pollich

The /me endpoint solution doesn't work with a stateless API.

Collapse
 
bhaibel profile image
Betsy Haibel • Edited

Can you clarify what you mean by a "stateless" API? If I'm reading you right, the concept seems kind of nonsensical to me. I'm going to respond to what I think you're saying, but I'm worried that I'm constructing a strawman in doing so.

I see three worlds in which a /me endpoint wouldn't be "stateless." The first is a world in which the current user is considered a form of state. Whether that's true in some absolute sense is an abstract philosophical question. The more practical question: if we're considering tracking the current user (ie "performing authentication") to be antithetical to a stateless API, then how are we supposed to then perform authorization? Is everyone just supposed to be allowed to do/see everything?

The second: if you're using a /me endpoint to retrieve user data, in lieu of retrieving it from a cookie, presumably some earlier interaction has logged the user in. I suppose there's implicit state in the fact that those two interactions must occur in order. But even if we're using bearer token auth, that token needs to have been generated somehow. Complaining about the presence of that state doesn't seem very practical to me.

The third: cookies are inherently stateful because they require session data to be present on the server. This is kind of true? But if we want to use a bearer token instead, presumably we need to have generated it. We can't generate secure bearer tokens idempotently or reversibly, so we need to store some kind of data about the bearer token on the server... which to me is not philosophically all that different from creating a session?

Anyway, again, I feel like I may well be arguing against a strawman here! If there's some fourth definition I haven't considered, I'd love to hear how you're defining stateless APIs and why you feel that that definition produces more useful/usable API designs.

Thread Thread
 
erebos-manannan profile image
Erebos Manannán

cookies are inherently stateful because they require session data to be present on the server

Not exactly true. Many cookies use signatures or encryption to make the data trustworthy for the server, so there does not need to be the somewhat traditional "sessions" list in a database.

Thread Thread
 
bhaibel profile image
Betsy Haibel

Cool, I didn't know that! How does credential revocation work in that context? Or do you just maintain a really short validity window?

Thread Thread
 
erebos-manannan profile image
Erebos Manannán

Well, it doesn't necessarily work. One way is to keep short validity, another is to tie it to e.g. the last update of the relevant user's password, or similar - you wouldn't have to check the contents of the package, just that it's been signed after the password has last changed.

There are lots of little options you could use, but it's not always necessary.

Thread Thread
 
bdruth profile image
Brice Ruth

I think lietu has just described JWT in a cookie ...

Collapse
 
erebos-manannan profile image
Erebos Manannán

I don't really understand what your thinking of stateless is here. If your cookie holds all the necessary data, e.g. your user information in encrypted form or JWT style tokens or similar, then your backend server really needs to know no other "state".

Thread Thread
 
kpollich profile image
Kyle Pollich

To be clear, you're suggesting using cookies for authentication in a REST API? In my experience this is not a successful pattern and certainly doesn't conform to some of the core principles of REST.

Thread Thread
 
rdegges profile image
Randall Degges

It does conform to REST. There's nothing not RESTFUL about cookies.

Cookies are purely an HTTP header. When a cookie is sent from a browser->server it is sent via the Cookie: name=value format. This is it.

What you're comparing it to is the Authorization header, which looks like this: Authorization: .... There is NO difference whatsoever here except for the name of the header.

This is a really common misconception. I've started replying to comments (this article really blew up while I've been busy working...) but I plan to write a more in-depth article covering some of the arguments I only briefly mentioned here in more depth.

Thread Thread
 
erebos-manannan profile image
Erebos Manannán

On top of that, not all APIs have to be RESTful and assumed to be accessed by 3rd parties.

If it's your own application, you can still make pretty RESTful APIs, using cookies, and not have to worry about the difference between using Authorization: ... headers vs. Cookie: ... headers. Authorization: header is great when you assume the client is a non-browser application, and you can cater to those easily with your middleware. For your own browser applications cookies are better.

Thread Thread
 
bdruth profile image
Brice Ruth • Edited

Keep in mind that some sandbox environments don't allow arbitrary headers to be set or even cookies to be added, but they do support the RFC for Authorization headers. So, it may seem like the same thing, but when your local API (e.g. WkWebView had (has?) this as a limitation) - then you're going to have to use it that way.

Thread Thread
 
erebos-manannan profile image
Erebos Manannán

So what you're trying to say is that since some systems are limited, it's ok to use a bad practice everywhere?

Of course you're allowed to work with the limitations of your system (though finding a less idiotic system is a better choice), but that doesn't mean you should use the bad practice from the bad system when working with good systems or systems you have control over and can fix.

Thread Thread
 
tuanmh profile image
Tuan Minh Huynh

Good article, but the language is too strong. You made a few good points but also some arguments are controversial.

I am not sure if using authentication cookie for API is a common pattern out there. I haven't seen many APIs using Cookie for authentication. Cookie and Authorisation are designed for different purposes. Yes, they are just different names behind the scene but applications treat them differently. Authorisation header is not automatically pre-filled by browsers while cookie is. That means using Cookie as authentication you're prone to CRSF. That's why we use Authorisation header to avoid CRSF as much as possible.

And last point, no one puts a few MB of data in a JWT token.

Thread Thread
 
erebos-manannan profile image
Erebos Manannán

I hope nobody is putting a few MB of data in my localstorage either. If you use Authorization header, the token can be extracted by XSS etc. and sent to malicious servers and they can then use it however they wish.

If you use a secure; httpOnly cookie it can't be stolen by malicious JS and is bound to your browser's security model. Add to that proper CSRF tokens, and e.g. sameSite=strict and you've got a decent system.

Collapse
 
jkoudys profile image
Joshua Koudys

Why do you assume I have access to a an endpoint where I can fetch this user's information? This user's claims could come from a trusted 3rd-party, and their claims are verified on my end by their pubkey.

Collapse
 
isaacdlyman profile image
Isaac Lyman

Thanks for introducing me to IndexedDB. I had been using localStorage in a webapp and IDB sounds like a much better solution.

Collapse
 
rdegges profile image
Randall Degges

IDB is great for some use cases =)

Collapse
 
philnash profile image
Phil Nash

Hey Randall,

I definitely agree that storing session information in localStorage is a bad idea. Secure, HTTP only cookies are the way forward for that.

I just wanted to point one thing out around cookies. You said

One of the annoying things about cookies (the only real alternative to local storage) is that they need to be created by a web server. Boo! Web servers are boring and complex and hard to work with.

Firstly, you definitely come up with another alternative to localStorage later in the article with indexedDB. However, you absolutely can write cookies from JavaScript. It's as straightforward as:

document.cookie = "dev.to=awesome";
Enter fullscreen mode Exit fullscreen mode

You can then read the document's cookies again with:

console.log(document.cookie);
Enter fullscreen mode Exit fullscreen mode

Now, all we need to do is flawlessly fix XSS and we won't have to worry about any of this again!

Collapse
 
mechpave profile image
Pavel Mechovič

Doesn't this approach have the same security flaw as storing an authorization token in the local storage? I mean, every JS code is going to be able to read them.

Collapse
 
philnash profile image
Phil Nash

Well, yes. As Randall points out above, he was referring to httpOnly cookies, which cannot be read or written to from client side JavaScript. I think that paragraph just lacked that context. Client side cookies are just as susceptible to XSS attacks as localStorage. I just didn't agree that just cookies needed a server to write them, httpOnly cookies do though.

Collapse
 
rdegges profile image
Randall Degges

Good point -- I was referring to the httpOnly ones. Good catch!

Collapse
 
greenhatman profile image
Albert Cloete

If a rogue script can send the contents of local storage to a server, can't they just add a key logger as well? In other words, if someone can inject malicious JavaScript into your site, you've got problems no matter what you do.

Collapse
 
adisrika profile image
Srikanth Adimulam

Title of the article could have been "Please Stop Using Local Storage For Sensitive Data" rather than "Please Stop Using Local Storage"

Collapse
 
danmilward profile image
Dan Milward 🌈🕹

YES!

Collapse
 
sackingsand profile image
Yudha Putera Primantika

If it's not sensitive, why would you even store it?

Collapse
 
yak profile image
Anil

This story creates so much FUD(Fear, Doubt and uncertainty) about a very common tool like Local Storage which is very essential for a lot of web applications.

Careful research on this topic will tell you that there are no known Local Storage vulnerabilities that lets some one else steal your data from the browser's local storage.

JWT is an awesome idea that works well for scaling many services.

Storing JWT tokens in local storage is perfectly alright.

Storing ids in Local storage is perfectly alright unless it is something like social security id. Even if some developer did store social security id on local storage, it is not so easy to steal that information from local storage unless the end user has malicious browser plugins. If the end user has malicious plugins then nothing will help.

There is a place for encrypted cookies, local storage, indexdb and server side session storage. There is no one size fits all approach to all problems. In many many cases, storing temporary data like ids and user preferences on that site are perfectly alright.

These ideas belong to the author and definitely not the employers, but I cannot help it, after reading this, I have a very poor opinion of the employer. I doubt I am the only one who thinks like that.

Collapse
 
rdegges profile image
Randall Degges

Hey there,

Thanks for leaving a comment!

I understand what you're getting at here, but I strongly disagree. I'm one of many people actively involved in the security community with this viewpoint.

The creators of the JWT spec itself, for instance, did not design it for the way it is being used in web apps described here. There are TONS of resources written by cryptographers (like DJB), security professionals, etc., all pointing to the massive holes and problems that using JWTs and storing tokens in local storage can cause.

It's not just me.

The biggest problem I see is that there is so much misinformation around advocating using these things. This is a big problem.

This article is just one of many which showcase some of the issues with all the insecure recommendations made amongst the general web development community.

I strongly urge you to read through all the other resources out there which have more to say on this topic. Not only does OWASP recommend against using local storage (which was the point of this article), but there are a PLETOHORA of reasons why you don't want to use JWTs AT ALL! The spec itself has SERIOUS issues and has been widely criticized by the security/cryptography community.

The underlying crypto options themselves have a million issues that cannot be easily resolved, and that's all BESIDES the portion this article talks about, which is just the high level architecture/storage piece.

Collapse
 
yak profile image
Anil

Best way to refute my argument is to provide some references where JWT was unsafe or local storage was compromised.

Instead you are still continuing the FUD and throwing a bunch of other accomplished people's names and complicated crypto words and "security community" to bring credibility to your arguments.

I think you should talk about this to some other experienced people at your work place. People with serious qualifications disagree with your assessment of the topic at hand.

Thread Thread
 
rdegges profile image
Randall Degges • Edited

Here are some good resources to read through:

And, re: when is local storage compromised... Read through OWASP top 10: owasp.org/images/7/72/OWASP_Top_10... (XSS has been one of the top 10 web exploits since 2013 and likely far before).

Thread Thread
 
yak profile image
Anil

Did you even read these blogs you linked to?

The first link says "This article does not argue that you should never use JWT". but you are confidently advocating everyone not to use JWT at all.

The other links do not show any vulnerabilities in either local storage or use of JWT tokens. They are talking about specific cases of using bad encryption mechanisms with JWT payload.

If you have XSS in your setup, using local storage is least of your concerns.

The links you posted are related to security. None of them support your argument that local storage and JWT are bad.

Thread Thread
 
rdegges profile image
Randall Degges

Yes -- they do.

The first link describes what is talked about in this post. Using JWTs for web auth is not optimal and causes a lot of issues.

The other links talk specifically about weaknesses in JWTs as a protocol, implementation issues, and real security concerns with the spec.

The bit about OWASP + XSS is the main issue around local storage, since it's something difficult to prevent, even when you're using CSP, which I explained in depth in this article itself.

I'm not certain what more you are looking for =/ The usage of JWTs for web authentication tokens is not ideal, and even for valid use cases of JWTs they are not optimal to use due to security issues with the protocol (as well as many popular implementations) you would be better served using alternative token types which avoid JWT issues.

In short:

  • It is not recommended to store sensitive data in local storage due to wide prevalence of XSS and the difficulty of preventing XSS
  • JWTs are not recommended due to spec issues, poor encryption and signing algorithm choices, complex specs that make developing JWT libraries challening, and flexibility in algorithm usage which allows web developers to easily mess up important implementations

Not sure what else I can do to explain :o

Thread Thread
 
yak profile image
Anil

You are able to talk around the topic in circles while missing the whole point. You are advocating people not to use local storage and JWT tokens based on nothing credible or nothing concrete. You are misleading a lot of people with your story.

There are perfect legitimate use cases for JWT and Local storage. They are very useful tools.

There are no spec issues with JWT and you can use JWT without encryption.

XSS is not an issue with local storage.

Please stop the FUD.

You do not have to explain anything, If what you say is credible and has any merit, people will make up their mind.

Thread Thread
 
rdegges profile image
Randall Degges

What you are saying is directly contrary to what is outlined in those links I supplied.

I'd love to have a more in depth convo at some point. I'd you're interested in continuing the conversation please hit me up: r@rdegges.com. I'd be happy to talk about spec issues in more depth, or issues with architecture as outlined above.

Really open to whatever! It's a complex topic, and fun to look at.

Thread Thread
 
worc profile image
worc

looking back, this sure seems more like a hit piece from a shill than a legit criticism of jwts

Collapse
 
rdegges profile image
Randall Degges

Heyo! I can tell you obviously aren't a fan of the article.

One thing I would say though: just check out the point of the article. I'm not telling people to do anything insane. I'm trying to encourage people to simplify their apps, their code, and often their security strategy by simply using cookies.

They can both contain the exact same info, transmit the same data (in the same way! via http headers), and provide very little differences in terms of implementation/API.

What I'm suggesting here is simpler, quicker, and more secure than what I've described as the problem.

And... I'm not the only one saying this stuff. OWASP, Thomas Ptacek (a well-known security researcher), and many other smart people constantly try to dissuade people from doing the things I mention above. This isn't anything obscure that I'm talking about.

Collapse
 
hackshed profile image
Hackshed

As someone who is taking their very first steps into web development, I found this article was very informative and I like the repetitive way the fact is drummed into the reader. I know that when I get to that stage in my learning, this is something that will come to mind straight away. Thanks :)

Collapse
 
rugk profile image
rugk

Sorry, but as far as I see the whole point of this article is (for the security argument) to not use local storage, because of XSS attacks.

XSS attacks are a serious thing, but recommending cookies is just silly. Obviously if you have an XSS attack JavaScript can also read your cookie. Even more, it can easily scrape your website and such stuff.

So if you have an XSS vulnerability you have a problem. Period.

OWASP, as you quote, points out a different security aspect, however: Data is locally stored. (obviously, that's how the thing is named). As such other local users may access it. If that surprises you, I think you did not get the concept of a "local storage".
As such, obviously, I agree to not put credit card numbers or other sensitive data in there, which may require a login.
However, what's for sure is that a cookie itself is not really much more secure than local storage. The only advantage it may have is that it can be automatically deleted at the end of a browser session, thus invalidating your own session. But as it seems, JWTs also found a solution for that.

Collapse
 
rugk profile image
rugk

Tom add: Yes, httpOnly (and sameSite) cookies add some security and when you can use them, it's great (for session cookies e.g.)! However, it's also a thing you need to use. You can also use local storage as secure as this.

My point is just, this article calls out local storage as insecure and only shortly explains how cookies should be done to be more secure! It's not at all obvious that when you don't do it properly also cookies are by no means more secure.

Collapse
 
thechrisjasper profile image
Chris Jasper

I disagree on the security concerns with LS and JWT. As others have said, you will have a much larger concern if an attacker can inject JS into your site. Proper use of JWT is implementing expirary and sensible authorization after one has authenticated.
Deferring to the Auth0 docs, they recommend LS or cookies for storage.

Collapse
 
kpollich profile image
Kyle Pollich

Deferring to Auth0 is a great call here.

auth0.com/docs/security/store-toke...

Collapse
 
rdegges profile image
Randall Degges • Edited

I'm very familiar with Auth0 and have a lot of friends there. That being said: the recommend incorrect things all the time.

Nobody is perfect, especially not security companies! We get things wrong all the time ^

Collapse
 
suhas_chatekar profile image
Suhas Chatekar
  1. They do not recommend, they say you "can" use local***
  2. Right after that they say, you can use cookies too
  3. For both options they tell you what can go wrong
  4. For the local storage option, they mention XSS, exactly the thing this article is trying to educate us about
Collapse
 
masoumii profile image
M.M. • Edited

What a horrible title to use for your article.

You could have used "Please stop using local storage FOR SENSITIVE INFORMATION".

besides that, its an amazing API which allows us developers to make cool things you couldnt do without ( at least not that easy ).

Examples of localStorage uses I have used:

  • Website theme changing

  • Client information such as duration of visit per page ( you save this in localStorage so you can send this info to the server with AJAX on next page visit sendBeacon has limited support, on unload doesnt seem to work persistently)

  • Device specific settings on a website that cant/ dont need to be saved in externally.

I could think of many other uses for the great localStorage API.
simply, it can store anything ( ofcourse dont store sensitive information ) which doesn't need or can't be saved to a server for whatever reason.

Collapse
 
prodigalknight profile image
RevanProdigalKnight

While I agree with most of your points, there are hacks or workarounds to get past most of the limitations of localStorage and sessionStorage.

At my last job, I was tasked with writing a SPA, but the person in charge of the backend was an absolute idiot who would rather re-use old, inefficient code than write new code, so I ended up using sessionStorage to store a lot of information (we worked with PHI [Protected Health Information], so this was really a terrible workaround in practice).

Since we had to work with IE10, I couldn't implement an indexedDB solution to the problem, and ran into several issues, firstly, the lack of security. In order to address this, I incorporated Crypto.js into the webapp and encrypted everything before storing it and decrypted when retrieving it. The key to access sessionStorage was created anew every time the page was refreshed, and if a key couldn't be read with the stored key, the data would be deleted and re-fetched.

This led to a second problem: some of the data I needed to store was quite large, and encrypting everything meant I had to base64 encode the result in order to stuff it into sessionStorage, which led to a 33% bloat on everything. Sometimes, this would blow out the 5MB limit. So I ended up incorporating a GZIP library to compress the data when storing and then decompress when retrieving.

This led to a third problem: GZIP isn't the fastest algorithm, and this was all being done synchronously, so when compressing/decompressing large data sets, the browser tab would freeze (we had some searches that could return 3-4MB of data). Fortunately, IE10 at least supports Dedicated Web Workers, so I was able to offload the encryption/compression and decompression/decryption to a worker thread. The worker took the data to be encrypted/compressed/decrypted/decompressed and an encryption key, and returned the result.

Due to the sheer amount of data we could end up processing in a short period of time, I also ended up writing a worker thread pool manager so that we could have 2 or more dedicated workers to do that and not cause lag spikes when doing lots of reads/writes to and from sessionStorage.

Eventually, the solution I ended up with was as follows:

  • Generate a random encryption key every time a user logs in
  • Store that key in sessionStorage using a different, fixed encryption key (insecure, but only accessible inside our own code)
  • Whenever a request that can be cached finishes, send the data off to a worker thread to be compressed, encrypted, and then compressed again (this yielded the best overall size - one 3.5MB JSON payload was compressed to a little over 220KB once)
  • Store the result in sessionStorage
  • Reverse the process to retrieve
Collapse
 
adamwknox profile image
DrKnoxy

I hope this is a joke

Collapse
 
prodigalknight profile image
RevanProdigalKnight

No, that is not a joke. What part of it made you hope that it was a joke? Perhaps I can clarify.

Thread Thread
 
rdegges profile image
Randall Degges

There's a lot of security issues in the architecture you described above:

  • Using crypto in client-side JS
  • Storing an encryption key on a client
  • etc.

There are other ways to do this stuff safely, although I don't envy your situation.

In a lot of cases focusing on security isn't possible: maybe it's due to a very bad team dynamic (like back-end developers not wanting to work with you), maybe it's due to legacy issues -- but whatever it is, it isn't always feasible.

I like to keep things simple and try to focus on security for apps that require it -- and in these cases I just do the most basic straightforward thing possible.

If you ever find yourself using encryption tools manually (especially in JS) you may want to re-evaluate your goals and see if there's something simpler you can do.

Thread Thread
 
prodigalknight profile image
RevanProdigalKnight

I didn't really have a choice. The users wanted the SPA to be fast, my bosses wanted it to be secure, and the backend engineer was unwilling to address his performance issues. I had to compromise a bit.

Collapse
 
alex_escalante profile image
Alex Escalante

Well, you stated that using Local Storage for sensitive information is bad, and that's OK. But just saying "do not use" doesn't constitute good advice. There are good and bad choices, as always. Know your choices and act accordingly.

Collapse
 
rdegges profile image
Randall Degges

Totally! I agree.

Collapse
 
alex_escalante profile image
Alex Escalante

I like to use github.com/localForage/localForage because it uses IndexedDB or WebSQL but it falls back to local storage when needed. Still, security advice holds.

Thread Thread
 
danmilward profile image
Dan Milward 🌈🕹

This looks awesome. Thanks for sharing Alex.

Collapse
 
meisekimiu profile image
Natalie Martin

I've found one of the best uses for (local|session)Storage is just to cache data. If you have a data set that is going to remain static for the entire application session but still needs to be fetched asynchronously, sessionStorage is great for caching it (unless it somehow contains sensitive data of course...).

Collapse
 
rdegges profile image
Randall Degges

Exactly! I love it for that. This is what I use it for as well.

Collapse
 
lsegal profile image
Loren Segal • Edited

A couple of technical corrections here about your claims that LocalStorage is "basic":

1) LocalStorage CAN be asynchonous.
2) LocalStorage CAN work with web workers.

#1 is actually possible by taking advantage of #2. By setting up a separate frame/JS exec environment, you can use standard web worker communication mechanisms of postMessage to communicate across your site. I've used this in a few apps to implement shared storage across local/remote access of a site. Doing this gives you async access, which may or may not be a good thing depending on what you're doing with localStorage, but if you're accessing a lot of data from storage it's wise to be using this method anyway.

Finally,

3) While it's true that you can only store string data, treating this as a negative is subjective. MOST key values stores (or DBs in general) treat structured data this way: MongoDB, BoltDB, even storing JSON in SQL, in all cases you're doing exactly the same thing. Sure, there's a downside for numeric primitives which SQL supports natively, but that's quite a small edge case (and for JS, passing floats around as string types is much safer). Performing serialization is a pretty standard practice in structured data cases.

And of course, having to serialize structured data is actually a decision you're making in the first place-- you can always avoid heavy serialization by restructuring your storage (using keys to namespace structures), at which point you're really only converting numerics, and that's again almost rarely a bottleneck in the grand scheme of all things JS.

I would actually argue that storing data as strings is a feature, not a bug, and a pretty standard practice for KV storage, too.

There's also a small point to be made about the 5MB limit (actually 10MB these days but who's counting) which you can workaround using methods 1&2 (granted very awkwardly), but also, you can use IndexedDB for larger data sets if you need to, you should probably only be using localStorage for state, not novel user data (like "saved content").

Collapse
 
pocketstealer profile image
Pocketstealer

For this reason alone we shouldn't use internet...

If you have xss on your page... there are worse things than a token.

What they can do with a token? Access the account? Then what? Bohoo nothing.

You can ask for password for "confidential" data that really hurts the user. Every bank does it, you can do too.

The only reason is XSS. And that it's mitigated by any good framework out there(Symfony, Laravel, etc).

JWT tokens can be used everywhere. Not everyone has the luxury to have everything on one server.

JWT are insecure if you don't know how to use it. You can pretty much make a JWT 5 minutes required refresh and 24 hours expiring...

More than that if you really don't like them being stateless(but thats another problem) you can put an id on them and save in database but that defeats the purpose.

You can attach a ip to the JWT. Makes it even harder to steal.

I mean i lock my door when i leave the house. I don't put a tank in front of it, hire 20 bodyguards and buy a flamethrower ...

Collapse
 
suhas_chatekar profile image
Suhas Chatekar

How do you attach an IP to a JWT? And even if you manage to do that, how do you then use an IP bound JWT from an SPA or mobile app?

Collapse
 
pocketstealer profile image
Pocketstealer

Simple. You can put it in the jwt ...

JWT can have any number of object you want.
It's a json...

Any request to an has a requester data... you can particularise from there, or any other method and make it very unique for user and harder to just take it and reuse it.

Did i say you have a universal solution? No. You need to make it your own.

If you are afraid of tehnology than thats on you. But tools are there to be used not be afraid because of "problems".

LocalStorage has some drawbacks, but has good parts as well... Tokens are used everywhere ... even to log in on this websites.

Lets not talk the iframe solution which is implemented and has security flaws as well(looking at you google).

Collapse
 
akinzeman profile image
akin zeman

depends on the value of your project or house..

Collapse
 
dheitzer profile image
D

You're advocating for cookie-based authentication using sessions (an increasingly dated practice), but do not mention anywhere adding real csrf protection:

From OWASP regarding your SameSite=strict suggestion
github.com/OWASP/CheatSheetSeries/...
Considering the factors above, it is not recommended to be used as a primary defense. Google agrees with this stance and strongly encourages developers to deploy server-side defenses such as tokens to mitigate CSRF more fully.

Collapse
 
jfrankcarr profile image
Frank Carr

Local storage can be useful in some scenarios as long as one is aware of the security situation and other limitations.

I used local storage on a single page application where the user had the option to play around with various scenarios in a parts configuration/selection quote before committing it to the server/database. There wasn't anything that needed to be secured in there, just the user's sandbox so that they could reuse it as a template for another customer quote or finish their work on the quote the next day.

One big limitation on using IndexedDB and Cache API in the average corporate environment is that many users are still on IE or Edge which don't fully support them.

Collapse
 
slang800 profile image
Sean Lang

Any JavaScript code on your page can access local storage: it has no data protection whatsoever. This is the big one for security reasons (as well as my number one pet peeve in recent years).

Ok, but if you're dealing with an XSS attack then any JavaScript on your page can also make network requests using your cookies. Sure, the tokens themselves haven't been stolen, but the attacker can still use them however they want from right there in the browser.

 
ashleyjsheridan profile image
Ashley Sheridan

The whole point of this article is to raise awareness of security. Sadly, I find too many front-end devs think that security is something for backend devs and server ops. I understand your point on polarity of these types of discussions, but security is really not something to screw around with.

Collapse
 
suhas_chatekar profile image
Suhas Chatekar

First of all - I agree with what you say in the article and fully echo your sentiment. I am personally on a journey to find the best solution for the problem.

I am sure you would appreciate the complexities of building software in larger teams. We often have a situation where the API is built by one team, the front-end by another. These teams do not talk to each other. Not that they don't want to. It's just how they are structured. And both depend on an identity provider (something based on OIDC or OAuth 2.0) who is responsible for issuing the tokens. This very same API can be consumed by another team that is building a mobile App.

Now, I can take your advice and put my JWT in an HTTP only cookie. My mobile app can add a cookie header to make sure that JWT is always sent to the API.

You would also appreciate the fact that running an XSS attack on my SPA front-end is only one of the many ways to steal tokens. There are a number of documented vulnerabilities in both ODIC and OAuth 2.0 that the hackers can target to get hold fo tokens. Once they do, it does not make any difference if the API expects the token to be set in a header called Authorization or a header called 'Cookie`.

Is there a way for the API developer to protect its API without having to worry about the security mechanics in place at the consumer?

Collapse
 
rdegges profile image
Randall Degges

No. Unfortunately, if you are using OAuth or OIDC that's the name of the game.

I'm not a fan of either protocol due to to their poor structure and implementation issues. The burden in using these protocols is everywhere: on the authorization server, on the client, and anywhere else :(

If you're stuck using those protocols just do the best you can with the tools you have.

Collapse
 
cowbert profile image
cowbert • Edited

The very fact that you're posting on dev.to means you're already using OAuth, as it's the only auth mechanism for the site (via either Twitter or GitHub's IdP). There's only a few ways to implement CSRF-less and cross domain credential proxying and JWT is one of them. I don't necessarily agree with JWT, since by the time you get done implementing revocation and audit logging, you're just a hop and a skip from just having the IdP store "session" since at the end of the day either the JWT itself or a token wrappered with a cookie represents the user's identity to the application. If you're not facebook or some other huge platform with potentially a gazillion identity verification requests per second, signed JWT doesn't save you much compute anyway since it's expensive to generate the token signature vs. just having the IdP do a session lookup against something fast like Redis. On the other hand, you're not autospewing the contents of your cookie jar on every request to that domain either.

Collapse
 
suhas_chatekar profile image
Suhas Chatekar

Why would you say that? I have been using OIDC for some time without any significant issues. Can you shed some light on why you think these protocols have problems?

Collapse
 
chimon1984 profile image
Ryan Edge • Edited

Some clarifications on JWTs taken from the RFC as well as JWT.IO and Auth0's best practices for storing tokens.

They can be used to store sensitive information.

From RFC:

A JWT may contain privacy-sensitive information. When this is the case, measures MUST be taken to prevent disclosure of this information to unintended parties.

From JWT.io:

JSON Web Tokens are a good way of securely transmitting information between parties. Because JWTs can be signed—for example, using public/private key pairs—you can be sure the senders are who they say they are. Additionally, as the signature is calculated using the header and the payload, you can also verify that the content hasn't been tampered with.

They can be used for authentication and that is one of their primary use cases.

From JWT.io:

Authentication: This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign On is a feature that widely uses JWT nowadays, because of its small overhead and its ability to be easily used across different domains.

I think it is important to point out that there are security concerns with both Cookies and JWTs (mentioned in Auth0's best practices) that cannot be left unchecked and should be handled delicately.

Collapse
 
rdegges profile image
Randall Degges

Auth0 is a great company (I have a lot of friends that work there) -- that being said, they recommend things that other security experts disagree with all the time.

Nobody is perfect.

Auth0 is the main reason JWTs are so popular today, and has a pretty vested interest in encouraging people to use them for authentication (which I strongly disagree with).

There are a lot of resources online of cryptographers speaking against using them for this purpose any why (other than myself).

Collapse
 
chimon1984 profile image
Ryan Edge

Don't disagree with your assessment of Auth0. My nit would be the RFC for JWT says quite plainly that there's nothing wrong with storing sensitive data in a JWT so long as it is safeguarded.

That said I don't see much advantage to using a JWT to store anything beyond claims & id.

Collapse
 
jgalentine007 profile image
Jonathan Galentine

I'm pretty new to JWTs and have been researching this issue a bit - the only OWASP recommendation I could find is to keep JWTs in local session storage and use a fingerprint cookie to prevent sidejacking? owasp.org/index.php/JSON_Web_Token...

Not sure if the link above is current, updated, outdated or what. I've also looked at some identity providers like Okta and Auth0 and all of their docs show using local storage for JWTs, but then there are blog posts (a few years old) from employees kind of arguing the opposite (even one who seemed to have done his research on stackoverflow!) - there seems to be so much conflicting information.

Sorry to renew a zombie thread!

Collapse
 
wwhurley profile image
William Hurley

LocalStorage is an option that comes with tradeoffs. Whether it is better for a given use case or not is going to come down to the specifics. We assume that we need to persist state between the server and the client. This means that our two choices to identify each request are a token stored in LocalStorage or one stored as a cookie. One is more vulnerable to XSS the other to CSRF. Personally I'm generally more concerned about CSRF, but your mileage may vary.

Collapse
 
rdegges profile image
Randall Degges

Thanks for the comment!

Yah, you can pick either. I personally feel like preventing CSRF is a lot simpler since it's built into almost every web framework by default nowadays. There's only way to CSRF a website but a million ways to XSS it :(

Collapse
 
wwhurley profile image
William Hurley

Well, I find I'm rarely in a place where I'm too concerned about allowing untrusted mobile code into my application. Yes, you can end up with bad actors in things like NPM but an XSS attack is the least of my worries there. And elsewhere it's just a matter of sanitizing user input. Sadly there isn't a silver bullet. Which is why we can't have nice things.

Thread Thread
 
rdegges profile image
Randall Degges

Hey!

Untrusted input is one vector -- but XSS comes from a lot of places: third-party JS (google analytics, etc.) -- domain compromise, DNS hijacking -- all over. It's significantly harder to prevent.

This article really only discusses the content from a web perspective, if you're doing native mobile stuff it's a whole different story =D

Collapse
 
itachiuchiha profile image
Itachi Uchiha

I almost used localStorage my every project. For example:

let OrderID = document.querySelector(".orderNum").innerText;
window.localStorage.setItem("OrderID", OrderID);

After that, I redirect another page without any query string. I still need to OrderID. This is the best solution at the moment.

I did not store any important information in localStorage. Yes, I know we shouldn't store any password or related information in localStorage.

What about OrderID like information? Should I use? I don't want to use server-side sessions.

For example, I completed Order. I delete key from localStorage.

window.localStorage.removeItem("OrderID");

Thanks for this great article.

Collapse
 
sebvercammen profile image
Sébastien Vercammen • Edited

After that, I redirect another page without any query string. I still need to OrderID. What about OrderID like information? Should I use? I don't want to use server-side sessions.

You've just discovered POST.

Collapse
 
rdegges profile image
Randall Degges

That seems fine to me!

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

I would be interested to see a followup post to give some substance to this statement (especially the scaling part):

And yes, you can most definitely scale up a large website using this pattern. Don't tell me that JWTs are “stateless” and “fast” and you have to use local storage to store them: you're wrong!

Also, if an attacker can run JS on your site, then they can also issue XHR requests which will automatically use the secure cookie for your API. It is a valid point however, that they can exfil JWT from local storage and use it from outside (by setting a fake origin header), whereas using HttpOnly cookie the attacker must call your API thru the infected browser.

P.S. Cookies are still vulnerable to CSRF unless you keep even more state on the server (anti-forgery token) and have the client send that state along with each request. Otherwise, your API can be compromised from other sites without your JS even being compromised. Maybe that will go away once SameSite cookies are widely supported.

Collapse
 
rdegges profile image
Randall Degges

I plan to do a follow-up post about this. Subscribe for updates =)

This post really blew up since I've been away @ work. There are a lot of cool writing ideas I've gotten since publishing this initial piece to dive into other related topics and help clarify a lot.;

Collapse
 
cowbert profile image
cowbert

Given a 2 minute expiration, and 1 request per second, Facebook for example, could nominally reduce the number of IdP requests (if they had to retrieve a session from the IdP) by almost 120 times (assuming an unsigned JWT, which isn't realistic, but is useful from a Fermi estimation).

Yes the main advantage of not using cookies is to not have to worry about CSRF, which can be quite complicated because minimally now you have to have the server also generate a non-HttpOnly cookie from the CSRF token in order for the JS to retrieve it in order to send it out-of-band as part of the subsequent XHR request.

Collapse
 
ksmithut profile image
Keith Smith

Really good article for opening the eyes of so many people. I want to get your opinion on something though.

So as far as I understand, it would be a bad thing to have a site that logs you in and sets a token in localStorage to be used for further authentication. This is bad because if you're vulnerable to XSS, the token can be used. One better alternative is to use a secure, httpOnly cookie (with SameSite, which isn't supported everywhere, but I'll give you that one because why the crap wasn't that in the original cookie spec, right?) with a JavaScript accessible csrf token. Aren't you still vulnerable to the same attack? In all of the ways that the csrf token is made available, it's accessible to the client. Either a metatag, or hidden input field, if it's in the DOM, it's accessible via JavaScript. Given, it's an extra step for the attacker to take, but any Pen Tester worth his salt will check csrf vulnerabilities in his first five minutes of testing. Just saying, XSS should probably be the focus in protecting your site because browser security is a bad joke.

I still agree with you, that localStorage should stop being used for authentication tokens and the like because support for localStorage can be spotty and unpredictable. I think it would be perfect to use localStorage as a local cache (of non-sensitive information).

Collapse
 
kavetu profile image
Manfredt Kavetu

Yes

Collapse
 
kavetu profile image
Manfredt Kavetu

Yes

Collapse
 
alexlion profile image
Alex Lion

In order to comply with privacy legislation such as GDPR and Cookie Law, I would like to offer visitors an option to opt in or out of the use of cookies. The only way is to use localstorage for my tokens unfortunately.

Collapse
 
mikejg101 profile image
Michael Goodwin • Edited

I think a lot of the responders have missed a very big point. JWT's that gives someone god like full access to every single API endpoint is bad. We know this. This is regardless of whether it is in a SPA or whatever else. But a significant number of tutorials specifically illustrate how to do this. In turn, every user has all access or no access.

Now come on, we are better than that, right?

Then, we decide to put these tokens in a place where anything has unrestricted access to the value. Sure, someone can get a hold of the cookie or do some magic to send requests as if they are you. But why even bother if I can just read the value from local storage. Hey thanks for the token. This god like power token is mine and I will use it for the... oops, you didn't set an expiration.

I guess you'll invalidate all of your user's tokens by changing your secret. Weeeeeelllllllllllll, hopefully you didn't follow the tuts and store your keys in such a way that you have to do a new deploy. Either way, now you just logged your users off in the middle of their purchase. Maybe they were customizing a product. Maybe they were finalizing a purchase. They click next and booooooommmmm!!!! They aren't logged in. I guess I'd go to amazon then.

If you were to practice being honest about security issue, you would tell me that you invalidated my login because another user's had their's compromised. I'd probably stop trusting you. Just stop for a second and say the words out loud. "I'm going to build a security layer that requires that I invalidate all users logins if a single user is compromised." Small app or big app, doesn't matter, that's just a terrible idea and should be reserved for catastrophic last resorts.

Now, if you have made it this far, you're thinking "Forget this guy, I know more than him because I'm a high IQ googler..." Well, here's a few others that just say no to doing stupid and careless things with people's secure data. Honestly, you all should be ashamed of yourselves. tsk, tsk.


auth0.com/docs/security/store-toke...
Browser local storage (or session storage) is not a secure place to store sensitive information. Any data stored there:

  • Can be accessed through JavaScript.
  • May be vulnerable to cross-site scripting.
  • If an attacker steals a token, they can gain access to and make requests to your API. Treat tokens like credit card numbers or passwords: don’t store them in local storage.

cheatsheetseries.owasp.org/cheatsh...
Local Storage

  • Also known as Offline Storage, Web Storage. Underlying storage mechanism may vary from one user agent to the next. In other words, any authentication your application requires can be bypassed by a user with local privileges to the machine on which the data is stored. Therefore, it's recommended not to store any sensitive information in local storage.
  • Use the object sessionStorage instead of localStorage if persistent storage is not needed. sessionStorage object is available only to that window/tab until the window is closed.
  • A single Cross Site Scripting can be used to steal all the data in these objects, so again it's recommended not to store sensitive information in local storage.
  • A single Cross Site Scripting can be used to load malicious data into these objects too, so don't consider objects in these to be trusted.
  • Pay extra attention to "localStorage.getItem" and "setItem" calls implemented in HTML5 page. It helps in detecting when developers build solutions that put sensitive information in local storage, which is a bad practice.
  • Do not store session identifiers in local storage as the data is always accesible by JavaScript. Cookies can mitigate this risk using the httpOnly flag.
  • There is no way to restrict the visibility of an object to a specific path like with the attribute path of HTTP Cookies, every object is shared within an origin and protected with the Same Origin Policy. Avoid host multiple applications on the same origin, all of them would share the same localStorage object, use different subdomains instead.

That's enough for now. Funny how OWASP recommends using cookies.....

As smokey the bear say's, "Only you can prevent people's secure data from being leaked by half reading documentation and trusting what that guy Steve said on StackOverflow." Ok, maybe Smokey didn't say that. But seriously, are you going to trust Steve? Come on, it's Steve.

(EDIT)

Just wanted to add a little more...

You wouldn't store passwords in local storage, right? If you just handed me a password, I'd still have to do at least some work to find out what user it went to. Think about it, just a password sitting out in the open can only do damage if I know the corresponding username. Does this hold true with tokens... No, Steve, it doesn't. A token by itself is enough, most of the time.

Collapse
 
panta82 profile image
panta82

Good information, it didn't occur to me that xss could be a problem for storing session id-s in a way that's visible to the browser.

On the other hand, if hackers get xss, they can still send ajax requests using the user's current session, or show a fake login prompt in your site's design or do a 1000 different bad things.

So http only cookie helps a bit, but you still really want to focus on preventing xss from happening in the first place.

Collapse
 
ilusionlinux profile image
Luis Morales

Exactly the same bad.

Malicious source injected to get the storage or to execute request using the cookies with the httpOnly flag enabled

Collapse
 
enijar profile image
James

If security is a concern, due to third party scripts having access to localStorage, then you can do something like this to remove third party access to it.

const Storage = {
  provider: window.localStorage,
  get(key) {
    return JSON.parse(this.provider.getItem(key))
  },
  set(key, value) {
    this.provider.setItem(key, JSON.stringify(value));
  },
  remove(key) {
    this.provider.removeItem(key);
  },
};
delete window.localStorage;

Storage.set("test", true);
Storage.get("test", true);
Storage.remove("test", true);

localStorage.getItem("test"); // will fail

This makes localStorage private to your Storage wrapper.

Collapse
 
madroneropaulo profile image
Paulo • Edited

Hello, Randall! Very interesting! I'd like to know, what do you recommend for static only websites? I want a stateless static website that consumes an aws lambda API. My users log in to a third party Identity Provider. This returns an IdToken and an Access token, these are verified by the lambda function with the Identity Provider, but yeah, you're right, there's no way to prove the token was not stolen...

Collapse
 
cmacu profile image
Stasi Vladimirov • Edited

As much as I understand the article and the reasoning behind it, I think the absent answer to this question makes it pretty clear that none of the solutions offered here is actually working. And this is a very very very common scenario (I just used it so I can submit my post here..). The fact that the author of the article doesn't provide any realistic solution just makes me frustrated and automatically discards any credibility he might have. To me this is just another rant over the way things are without any ideas or contributions of how to actually get it fixed. It's very easy to say "Use cookies and force the backend server to support them", but the reality is that all this is dictated by APIs that already have specifications in place and they very rarely support cookies. As front-end developers actually responsible of using localStorage, we have little to no control over that at this point.

Collapse
 
bennadel profile image
Ben Nadel

I have nothing to add to the existing conversation; other than to say this was a very thought-provoking post and follow-up set of comments. If nothing else, it has given me pause to stop and think about something that I may have just used blindly in the past. That said, I'm all-for session cookies. I always felt like JWT was solving a problem that I didn't have (I currently use session cookies to reference a server-side cache).

Collapse
 
gregfischer profile image
Greg Fischer

I'm with you Ben. But I do have a thought that I may have missed in all the conversation. To me this is partly about some simple CYA. If a breach occurs in a an app I worked on I want to be able to say I followed best practice where I could and within reason. Following OWASP advice would be wise, would it not?

Like Ben, I'm all for using sessions and have no need to use anything else. JWT doesn't solve a problem for me. (Until now, I may need to use some sort of token the client side framework needs to use). I appreciate all the different points of view though. In particular one person wrote about the need to balance security and usability, something we all have to do at times. But for me, unless I'm forced to, I see no need to change from old school sessions cookies, they're easy for me.

What's hard for me in all this debate is the completely different needs of some devs. seems like on one hand some of us work in much larger projects with multiple backends and teams. And on the other hand, there's people like me, where we have zero need for multiple backends, don't work with multiple teams, on small apps, have no apps/services that would ever need that, and no desire for it either. For me, I don't even get what people mean by stateless? (Isn't HTTP stateless by design?) My apps only talk to a single server and database. And the sessions stored on the backend lookup and validate every request, and its not slow (servers are pretty fast anymore, memory and storage is cheap). But I get it. If I were in the other situation with large scale deployments and backends I don't control, API's that have only one way to auth, I'd probably see it differently.

About SPA's, I've got an EmberJS SPA, that can run as a PWA, using standard form-based login and using sessions cookies. All while sharing the session with the older PHP server rendered pages so the user moves between old and new parts of the apps for certain legacy functions. (The older pages aren't tested with PWA integration though, yet). This is on a single server, however.

I suppose we all have to use the tools we've been given. :)

Collapse
 
duannx profile image
Nguyễn Xuân Duẩn

Minor point, you said that

For complex applications this is a big no-no as it'll slow down your app's runtime

I think it is not necessarily true. localStorage is synchronous. But it is incredibly fast enough to have no impact on overall performance at all.
This answer on stackoverflow did a benchmark and proved that.

Collapse
 
jenueldev profile image
Jenuel Oras Ganawed

I like this Article it is really informative and it gives me insights. But is there a way to secure the token in local storage? so that instead of using cookies, we can use local storage?

Yes, local storage is public, and people like us can able to see them and maybe try to edit the token, or copy the token and use it. I understand it.

But what if we try to make it secure?

ex:
token=jksdf2342jhc238740c29384htlsdgs8d70g9s848tuspirDFOdfGdgPOuoUS)(08)(}{[]dfglskldkfjg90450345

I created a function of my own that generates this encryption, that will be saved on the client's browser. This token consists of the client's ID, IP, expiration of the token, and a secret key.

Every time the User Login or use sensitive API functions like edit, and delete. The token is being checked first.

I made my own function to generate a token and a function that checks the token so that if this token was used from a different IP address it will not sign in. Or if the token expired, the person using this token will not sign. The users have to login again to generate a new token.

Collapse
 
paul110590 profile image
Paul Griffin

You say that:

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.

The implication is that if you don't store anything in local storage this scenario is totally fine, but of course the attacker can do anything they like, including log username/password combinations, so you are still completely screwed.

You also say:

Make sure that your cookie library also sets the SameSite=strict cookie flag (to prevent CSRF attacks)

And that's your only mention of CSRF. Well good luck to any readers who implemented that as their sole defence against CSRF.

Collapse
 
codingjlu profile image
codingjlu

Huh... I skimmed over this article and I think it'd be more appropriately titled "Please stop using local storage to store your sensitive data". That you would want to store whether a user's preferred theme is dark or light I would not consider sensitive data and could easily store it in local storage with no worries. Yes, local storage has its place, but local storage has its place: I don't care if it can only store strings; I don't need a binary executable in local storage to determine the user's preferred color scheme; I don't care if it's not secure: if a hacker manages to change the user's theme to light, so what? No credit cards leaked, no passwords leaked. I'm good. Local storage is blocking? I don't care: it's not slow, and I'm not trying to query 1003433 entries.

Is your title really saying what you're meaning?

Collapse
 
dominiquegarmier profile image
Dominique Garmier

The only thing that is probably correct about this article is that being open to XSS is bad. Of course you shouldn't use LocalStorage (for sensitive information) if you are open to XSS, but then again you also shouldn't collect any amount of sensitive information if you are open to XSS.

XSS would allow an attacker to run a keylogger of sorts, and in that case no matter how or even if you store sensitive information, that information is vulnerable.

Collapse
 
__ec73256df profile image
Виктор Щелочков

Ok but what about user preferences? Like what theme my user want. Should I really use sessionstorage which is automatically erase itself? Should i use cookies and enlarge bandwidth? And indexed db is probably overkill since i want just 1 string.

Collapse
 
nasht profile image
Nathan Hazout

Hi! I came to this old blog in light of recent efforts from browsers to ban third-party cookies.

I'm considering switching to localStorage specifically because cookies are loosing traction when dealing with multi-domain services.

Collapse
 
omarel profile image
Omar Elbaga

If you don't recommend storing a token in local storage because of security what makes it ok to store the session id of a cookie in local storage? No matter how you slice it, something sensitive has to be stored on the client side to authenticate itself to a server or backend api which still seems to be the example in the article.

Collapse
 
ozzythegiant profile image
Oziel Perez

After reading dozens of comments in this thread, I am nowhere closer to figuring out what's safer to to use for an SPA. Some have said that using authorization header is better to avoid CSRF attacks and is the way to authorize requests on mobile apps. Others said not to use it to avoid XSS attacks. Some have said to avoid localStorage because of XSS. Others say it's the same security level as a cookie, but then someone said if you specify the httponly and samesite flags, the cookies are stronger than localStorage. Some say JWT is not secure at all and wasn't designed for security, but yet others claim JWT is similar to using ssh keys. So then what? What does a developer have to do nowadays to get proper security in an app? From what I understood, the only two options I have are using JWTs in an httponly, strict samesite cookie along with a CSRF cookie, or use traditional session cookies.

Collapse
 
ghstahl profile image
Herb Stahl

A technique I have seen is that individual SPAs have to provide their own backend, hence cookie auth is in play. NO JWTs allowed. The SPA is in their own iFrame and there is a client side IFrame bridge that lets IFrames communicate between each other. The main communication is that my SPAs iframe could NOT do a silent SSO login, so it asks the parent app to do a LOUD login to grease the skids (so to speak) so that the SPAs IFrame will then succeed.

This came about because we had many different teams contributing wildly different plugins to the main website, and they all wanted to write their backends in different languages. Thee main website turned out to be just doing logins and mashups. So no real work other than Login and IFrame orchestration.

The Cookie Auth is Dead, long live The Cooke Auth!

Collapse
 
kpollich profile image
Kyle Pollich

Seconded about using memes and especially these awful ragecomic faces in articles where you're trying to be taken vaguely seriously.

Collapse
 
rdegges profile image
Randall Degges

Hey! I'm not going for a certain image here... I just like writing this stuff for fun.

I've been doing security/web stuff forever and I just try to write things that I'd enjoy reading.

Collapse
 
siloamx profile image
Marek Marczak

IndexDB is not very fast anyway. I worked with over 300000 JS objects using PouchDB wrapper. It was loading them during 200s (8GB of RAM, 3GHz proc). Totally unacceptable. Faster than SQLite? Nice joke! Implementations also can differ. There are many known problems with IndexDB using Firefox.

Collapse
 
petersf7 profile image
petersf7

Great article! And an even better discussion in the comments. I have a question regarding a topic Balmes lightly mentioned in his comment. When you want to make HTTP request to multiple backends the cookie with HttpOnly will refuse to be sent to another server then where it came from. How do you go about solving the issue with sending the JWT to several backends in a secure manner? Would you proxy the calls through the backend it was created thus taking a small performance hit?

Collapse
 
jeanlafitte17 profile image
Jean Lafitte

Your bias is showing. In one breath, you criticize cookie creation for being server-side, in the next, you're advocating for JS server-side solutions.

JS is a security nightmare and the problem is systemic; it's in the implementation and how it is designed and the security problems at this point cannot be divested from JS.

IF I have to use JS, I do so sparingly. Some recent and massive security breaches have happened because of JS. Interestingly, YOU could be watching what I type, delete, and whether I choose to submit this or not, all without my consent. I understand that it's your website, but, if I were to choose not to submit this, then it's clear that I didn't want what I had typed to go anywhere. JS can, and may, take that decision out of my hands.

Collapse
 
rdegges profile image
Randall Degges

Heyo-- that's actually the exact opposite of what I wrote here... I'm doing exactly what you say: I'm advocating for using server-side cookies and NOT storing data in local storage =p

Collapse
 
olu_semola profile image
Oladoyin Falana

I like reading post like this. Most of the time its not about the topic alone but the great comments that one can actually pick one or two things from. i'm a newbie in programming but i could at least follow along the arguments. In my own opinion, i think using JWT with a very short expiry should solve this. Also while i commend your view, i would love if you can share a concrete reason as to why in this case backed up with your experience other than say it because some great security guys are saying it. no one is perfect thou and the fact that is their profession doesn't mean they cannot be wrong.

Collapse
 
kyleljohnson profile image
Kyle Johnson

While I agree you should not store sensitive data in LS. For small non-sensitive data it is better than cookies or using any of these JS MVC frameworks that stores your model data in a JSON string in your page.

Collapse
 
iguatemigarin profile image
Iguatemi Garin

Nice article!
Wouldn't an encryption wrapper on localStorage solve the sensitive data problem?

Collapse
 
rdegges profile image
Randall Degges

No -- because so long as the data is being used (whether encrypted or not) as authentication data, it doesn't matter.

Collapse
 
iguatemigarin profile image
Iguatemi Garin

I see. So how could someone take advantage of encrypted data? Sorry if I'm being naive.

Thanks for the anser

Collapse
 
wwowwo profile image
wwowwo

All the commenters that say localStorage is safe is missing the point of session token hijacking vs hijacking an active session. If you got an XSS then your hijack is good for the time the user is interacting with the site at best as long as you don't lose your hook.

If you can hijack their session token you need milliseconds. You're in and out, to carry on your attack.

A lot of these web devs have not worked in the days prior to HttpOnly and on a site with a large number of page views that took in a lot of user generated content. Don't take HttpOnly for granted. Don't let your creativity for compound attacks to hijack a session token confuse the best defense mechanism for session hijacking attacks with a low barrier of entry.

Collapse
 
danielnoga profile image
Daniel Noga

If I understand correctly, OWASP recommends using sessionStorage for JWT, not cookies: cheatsheetseries.owasp.org/cheatsh...

Collapse
 
slang800 profile image
Sean Lang

And sure, you could serialize everything including data types into local storage, but that's an ugly hack.

Lol, what? That's not an ugly hack. That's how anything that isn't a core dump is written to disk. It would be tremendously inefficient and fragile to take JavaScript objects directly from an in-memory representation and then write them out. Unless you're working with a very specific in-memory representation (think Cap'n Proto) you need a serialization step.

Collapse
 
shirikodama profile image
Michael Thomas

I've been poking around trying to figure out where to put asymmetric keys generated by webcrypto to use for logging into web sites instead of using passwords over the wire. I've always been conflicted about using localStorage (which works fine because it's trivial to convert the keys to a pem string). You and others seem to imply that indexedDB is somehow more secure vs localStorage which would surprise me greatly unless there were some browser secured mechanism to access the db. Or are you not making that claim? IMO, if you have XSS issues, you have already massively failed so taking that off the table, I'm not sure what remains.

You can check out what I've been working on here:

out.mtcc.com/hoba-bis

Mike

Collapse
 
kkinder profile image
Ken Kinder • Edited

This information is debatable.

First of all, "storing" JWTs on a server-side session misses the point of what a JWT is and what a server-side session is. Think of a JWT as a stateless session key. You don't store session keys to lookup on a server.

Additionally, if any of those compromised JavaScript libraries runs malicious code, they can send requests to your server using the session cookie you marked as httpOnly.

Perhaps even more importantly, since a browser simply cannot set an httpOnly cookie at all, this advice presumes that there's never a PWA that does a credential exchange using an API call. You simply can't design a web app to modern standards and rely on your auth server setting cookie functionality, especially if you hope to target multiple platforms.

Storing a JWT in LocalStorage is mostly fine. What isn't fine is linking to offsite CDN's without subresource integrity. It isn't fine just slapping random bits of JavaScript code from random vendors all over your app. If you're doing that, you have a gaping security problem and no amount of httpOnly caution will change that.

LocalStorage is fine for secure data. Having an insecure app is not. Your rant is misguided.

Collapse
 
nicholasmabrams profile image
Nicholas Abrams

I believe that this article is a bit misleading. Given these facts:

  1. In many applications developed throughout the world, the JWT claims are used on the client as well as the server, so using the most secure form of cookie is out of the question for most. Also, cookie support in mobile can be wanky so the cookie solution seems best suited for web only clients. Also, the small size cap on cookies and serialization and parsing issues will add client performance overhead.

  2. document.cookie doesn't seem to offer any benefits over window.localStorage as far is it goes to XSS. The only benefit of the cookie is the SSL & HTTP only options, which should be enforced on the server to only serve over SSL and then use JWTs with the same benefit of always knowing they will we transferred over HTTPS.

Collapse
 
imichael profile image
✨iMichael✨

As you have hopefully learned since publishing this post: This topic is incredibly nuanced and titles like "Stop using X" just add to the cancer that is cargo-cult thinking that infects our industry.

A more thoughtful way to educate people on this topic is frame the trade offs of all storage options. In a SPA context there are perfectly valid justifications for using localStorage and as of this date, I'm not aware that anyone has ever demonstrated a real world exploit of a site that stores tokens in localStorage (in a way that is an exclusive issue with localStorage).

Collapse
 
nisugden profile image
Nicholas Sugden

Aren't you just highlighting the fact that people should not include third party scripts on their webpages if they wish to ensure that their sites are secure? Your argument relies on there being malicious content in a third party script. If this were the case (as it has been in the past) then the malicious script is not going to care what security mechanisms you have or don't have because it can bypass any of them in any case.

Collapse
 
jwf3148 profile image
jwf3148

A digression: I build some web applications and web pages that are used as my personal tools. localStorage is very useful to me and is used by me only in that context. I offer this only because the dogmatic "don't ever" creeps me out...admittedly my use of localStorage is not in the context implied by this article...but even so, and again, the dogmatic "don't ever" creeps me out.

Collapse
 
the_one profile image
Roland Doda

For SPA as I have seen from all around the internet they are suggesting to store JWT in localStorage so the user can be logged in even when the browser refreshes. However, many but too many people have linked me to this article so I MUST stop doing that! In the beginning I was really scared since in the article many but too many times is mentioned to NOT use JWT at all and definitely NOT to store JWT in LS.
Reading the comments I got the confidence that it's fine to store it JWT in LS.
But still, I am not pretty sure if should do or not. This article is pretty confusing and since it has so many likes people are strongly stating that I should stop storing JWT in LS.

Collapse
 
cryptodev7285 profile image
cryptodev7285

For usecases where trusting any 3rd party with secrets is not an option (long term personal private keys), I'm rather interested what are tradeoffs when storing secrets in different ways only on client side.

In extreme case can store secrets on an airgapped disconnected computers, sign there, transfer to hot computer via USB or picture of QR code so secret never leaves. But what can be done to improve UX for those who prefer it and how much do we have to give up for it?

I am really curious about specifically friendlier choices on client side:

Then you can store data inside html like props, inside react state, querry strings, hash strings, local storage, session storage, global objects. What is visible to other websites open in same browser, what is visible to your network provider, what is visible to another user of your computer, can you isolate access to stored data to only specific request sources, how much access do browser extensions have (a lot). Even if I encrypt secret on client with pin I only memorized, how do I handle the cleartext secret when I use it and how to dispose of it safely, do I recommend users incognito mode, guest mode, chrome offline mode, sandbox or vm - wide range of choices but haven't figured out yet what is worth the difficulty.

Keep searching but most advice is for server based apps to trust servers :(

Collapse
 
tannakartikey profile image
Kartikey Tanna

How to create "Remember Me" functionality if tokens get destroyed when the browser window gets closed?

Collapse
 
codingjlu profile image
codingjlu

"Remember me" data should be processed on the server with cookies.

Collapse
 
jrmcgarvey profile image
John R McGarvey

I agree that one should not store JWTs or other authentication tokens in browser local storage. The solution recommended here is to use httpOnly cookies. However, if a front end on site A authenticates to a back end on site B, and site B sets a cookie, that is a third party cookie, and the browser folks are on a path to eliminate these. It appears that the only alternative to browser local storage for this common scenario is going to disappear. Thoughts?

Collapse
 
khazbs profile image
Khazbs

Ok, but if I am developing an anonymous forum there is no sensitive data to store. In fact, I would like to restrain from using sessions to provide extra anonymity. Using localStorage makes perfect sense then, e.g. to store some display preferences and other data.

Collapse
 
zeerorg profile image
Rishabh Gupta

Now, I want to know, how BaaS handles these.

Collapse
 
passionate32271 profile image
passionateplayer

Stopping the use of local storage in web development is crucial for security, privacy, and user experience. Local storage, like cookies, stores data on a user's device, but it has limitations. First, it's susceptible to cross-site scripting (XSS) attacks, making sensitive data vulnerable. Second, it lacks expiration controls, leading to potential data clutter and privacy concerns. Moreover, local storage operates in a single-threaded environment, which can cause performance bottlenecks in modern web apps Gacha Nymph

Instead, developers are encouraged to use alternatives like secure HTTP cookies, session storage, or client-side databases that offer better control, encryption, and improved performance. This shift ensures safer and more efficient data handling in web applications.

Collapse
 
oyagci profile image
Oguzhan Yagci

Well… that was eye opening!

Collapse
 
assaultoustudios profile image
Vernon Joyce

Randall what’s your take on storing non-sensitive JSON responses from services in local storage?

Collapse
 
rdegges profile image
Randall Degges

Sure, go for it. Just serialize it into a string or whatever.

Collapse
 
crtl profile image
crtl

"When a user logs into your website, create a session identifier for them and store it in a cryptographically signed cookie."

Whats the diffrence to this between storing the jwt?

Collapse
 
grimaud profile image
Pierre

Storing JWT or session token is about the same sensitivity, so in my view the point is moot.

However, while I agree that for the sake of security, using properly configured cookies is the way to go, as far as compatibility is concerned, localStorage works in an iframe where cookies do not anymore. Safari killed third party cookies a while ago and Chrome is about to do the same - and even today, in a corporate setting, most people have third party cookies disabled by their admin. Only firefox has them working for good since instead of forbidding third party cookies entirely, they seggregate them by top level domain, handling the security concerns and allowing session cookies to work in an iframe.

So if you need your website/app to work in an iframe (hosted in another domain), cookies are not the way to go, it just won't work.

Collapse
 
varunalex profile image
Varuna Eeriyaulla

So what is the option for SPA?? Article is about "DON'T use" then my question is what to use?

Collapse
 
aniagit profile image
aniagit

Very informative and interesting article. Thanks, from now on I remember to avoid using Local Storage.

Collapse
 
jojupiter profile image
jojupiter

I think the only way to avoid an XSS attack is to put a two-factor authentication on critical operations

Collapse
 
kendofriendo profile image
Kenney G

Please stop using rage faces.

Collapse
 
andredias_1 profile image
André Felipe Dias

There is an older but similar article called "Stop using JWT for sessions" (cryto.net/~joepie91/blog/2016/06/1...). It is worth reading.

Collapse
 
andredias_1 profile image
André Felipe Dias

From the article:
"...However, local storage doesn’t provide any of the same security mechanisms that cookies do.

Local storage, unlike cookies, doesn’t send the contents of your data store with every single request. The only way to retrieve data out of local storage is by using JavaScript, which means any attacker supplied JavaScript that passes the Content Security Policy can access and exfiltrate it. Not only that, but JavaScript also doesn’t care or track whether or not the data is sent over HTTPS. As far as JavaScript is concerned, it’s just data and the browser will operate on it like it would any other data.

After all the trouble those engineers went through to make sure nobody is going to make off with our cookie jar, here we are trying to ignore all the fancy tricks they’ve given us. That seems a little backwards to me."

Collapse
 
csulit profile image
Christian Angelo M Sulit

Never???

..... mehh

Collapse
 
cutiko profile image
Erick Navarro

LocalStorage seems the equivalent of using SharedPreferences in Android as databade instead of the database.

Collapse
 
crazy4groovy profile image
crazy4groovy

Also, careful with IndexDB and IE compatibility.

caniuse.com/#search=IndexedDB

Collapse
 
fernandezjose profile image
Jose Fernandez

The comments are the real MVP!
I am here for the comments!
Article the coccoon, Comments the butterfly
We already have enough trash articles in Medium. Save Dev.to

Collapse
 
siloamx profile image
Marek Marczak

"Doesn't need to be used in an ultra high performance app" Another joke.
LocalStorge is about 10 times faster than indexedDB. Check it yourself. reyesr.github.io/html5-storage-ben...

Collapse
 
dvlp profile image
Pawel

localStorageDB gives you all the easiness of localStorage but 10x more space and is asynchronous because it's the smallest IndexedDB wrapper in the world github.com/DVLP/localStorageDB

Collapse
 
vsostaric profile image
vsostaric

What about session storage?

Collapse
 
rdegges profile image
Randall Degges

Same thing applies.

Collapse
 
issamseghir profile image
Flamingo

what does the user miss if deletes the local storage data with a "clean master" extension?

Collapse
 
afluegge profile image
Andreas Flügge

Great blog posting in the true sense: it started a fruitfull discussion. This post together with it's comments is realy helpful. Thanks for that!

Collapse
 
muslihiddin profile image
Muslihiddin

I think he saying to not use local storage in our projects to store sensitive data, I'm not sure tho😅

Collapse
 
chis0m profile image
chis0m • Edited

Wow great article, would like to write a Laravel/Vue followup on using the server-side session. How about that Rendall?

Collapse
 
jonatanvm profile image
Jonatan • Edited

The cache API doesn't encrypt the data so you are still prone to XSS attacks if you cache sensitive information. So it is not much different than storing sensitive information in localstorage.

Collapse
 
duongricky profile image
duongricky

Hi author, i am a Vietnamese, i really like this post, it's helpful for me, tks !!!

Collapse
 
arthur40056743 profile image
Arthur

what a long read

Collapse
 
thevetdoctor profile image
Obafemi

Basically, I think JWT usage requires a secret key, and this is usually stored in an env file.
How does any hacker get access to the secret key?
Pls correct me if am wrong.

Collapse
 
ashique12009 profile image
khandoker ashique mahamud

Then what might be its alternative?

Collapse
 
aisone profile image
Aaron Gong

Thanks for the great article. I had to stop using libraries such as nuxt-auth due to this issue, as well as lack of support for 2FA/MFA/OTP.

Collapse
 
ahmedsefiani profile image
ahmedSefiani

I think you are confusing people with your post

Collapse
 
foxcoding profile image
Konstantin Pfafenrod

Now Im gonna local storage even harder...

Collapse
 
kaburkett profile image
Kyle Burkett

Is everyone aware ADAL, Microsoft's enterprise javascript active directory auth library stores JWT in local storage?