EDIT: The title not being clear enough (I thought it was implicit): This article is about the "storing tokens in localStorage or in cookies for SPAs" debate. It is NOT about using cookies to store general purpose data.
I'm a tad tired of reading "dont use localStorage, it's not secure". Why ? Oh, yes "its accessible in JS". Let me tell you how this seems to be an overrated argument to me.
If someone owns you, someone owns you.
If someone can run arbitrary JS on your website, do you really believe a peremptible JWT will necessarily be the only thing that can be exploitable ?
Personally, I'd go with something far more interesting to me: Collecting what user are typing in password boxes. Or just performing the requests that interest me directly from their browser. Those exploits are even easier to write when using cookies, given that you dont have to guess where is the token stored nor how to use it.
Cookies also have their drawbacks
- Protecting against CSRF is not that easy, and beginners are not even aware of it.
- Implementation of multiple authentication is harder (if you're writing a signle API that must be usable in multiple websites)
- You cant control them (easily tell if you're logged-in, read associated data, ...)
- CORS is harder with them.
XSS / Malicious JS dependencies: The easy fix.
You're worried that one of your dependencies might be accessing your localStorage maliciously ?
Well, that's not a strong valid argument, you could just prevent it to do so like that when bootstraping your application:
const storage = localStorage; delete localStorage;
And voilà ! There is no more localStorage exposed in your window, but you still can access your tokens via the "storage" local variable (of course, you must keep it in a private scope).
These two lines of code will protect you from the most obvious and common exploit that localStorage is blamed for.
(of course you could imagine spying tokens by overriding fetch or equivalent, but that also is mitigable... its about preventing 99% of exploits, this is not strong security)
If other parts of your app need to access local storage, you could set
window.localStorage to a proxy that only lets the user access non critical parts of your local storage, leaving your precious tokens out of their sight.
Which one to use then ?
I think that the web has more suffered (and is still suffering) from CSRF attacks compared to stolen JWTs, so dont tell me that https cookies are the secure way to go. Unless you're writing ultra-secure and highly-reviewed code, no, they're not. Its easier to make mistakes with cookies compared with localStorage tokens.
Just to be clear: I dont prefer one over another... I'm just saying that this debate is a bit useless, both approaches having their own sweetspots and weakspots.
I'm just sick of people explaining you otherwise with a condescending tone just because they think they've understood all there is to know by saying "its accessible by JS". Or because they've read somewhere that localStorage tokens might be considered as a potential security leak by some. Give me a break. The world is about nuance, not about dogmas.
Happy to hear what you think of this in the comments.
Top comments (19)
Saying "don't use
I think from your article you meant that people say that
localStorageshouldn't be used as a storage mechanism for authentication/authorization tokens. That means that the real debate is session in a cookie vs 'token-that-will-likely-be-stored-via-a-storage-api-such-as-localstorage'.
That said. Using
localStorageis fine. Using JWTs is overrated and in most cases actually done insecurely. I don't think anyone should be condescending when mentioning this, but I think they often are because they don't understand why it's seen as problematic by those in SecOps.
Yup, you're right, I did not specifically mention that it was "localstorage as a mean to store auth tokens". I thought it was kind of implicit 😑.
I agree, things are most often actually implementd insecurely. That statement also applies to cookie usage, and lets be honest, to the pretty much everything when you're dealing with security...
It might have been the implicit intention, but I do think there is a big difference between discussing JWTs stored in localstorage or localstorage as a technology.
Regardless, Sven Slootweg made two compelling posts in 2016 pointing out why using JWTs (stored in localstorage) instead of using a session cookie is a bad idea, and I think he does it without being distasteful about it.
I think that, as developer, we should be aware of these points so that when we do choose to use a JWT stored in localstorage as a session token, instead of using a cookie for session authentication, we can actively guard that we use a good implementation. So instead of saying "everything is insecure", we say "we're informed, and decided that it's Good Enough™".
People who blatantly say "don't use JWTs" or "don't use localStorage" without elaboration or without understanding the nuance of each decision are honestly not worth your (or my) time. It's indeed pointless.
I don't agree that it's the same as tabs vs spaces, but it definitely has the same level of tribalism (and with that it becomes pointless shouting instead of constructive debate).
Yea... I totally agree to everything you've said, I've never said anything adverse to what you're writing, so I dont understand your comment. I'm only speaking about authentication tokens storage (which must be stored clientside... and yes, obviously validated by the server on each request)
[EDIT] Sorry, I've not seen that this was not intended to me... The notification did not mention it. Leaving my comment anyway :)
That only leaves the server, and that's the last place where you should want to store anything sensitive. Not only can you get hacked, just as the user can; said user doesn't even have any control to at least wipe the data if they want to be absolutely sure it won't be leaked.
Then there's the small but important factor that some data needs to be on the client, like session data, which can often be used to exfiltrate pretty much everything else from the server (although recently some large platforms have started to always ask for a password for certain operations, which somewhat mitigates this)
Hi, I would agree with a lot of the points made because like you said if someone is able to inject / run JS on the front-end of the app there definitely big issues already in play. Also, either
cookiescan be done insecurily. I find it interesting that no one seems is talking about
sessionStorageunless it assumed to be included with
localStoragesince they are similar in their access. But I would agree that
sessionStoragehas the benefit of being cleared on new tab.
"If someone owns you, someone owns you."
There are significant challenges for an attacker when authentication credentials are inaccessible vs. it is incredible easy to steal user data with access to credentials. There are layers of defensive controls in place (or should be in place) on a website.
This is a good page to get started on understanding the subtleties and various other security controls in place on websites.
"Protecting against CSRF is not that easy, and beginners are not even aware of it"
Agreed, there is a lot of room in the sentence you mentioned :)
My intention was not to deliver a detailed technical article, rather to express an opinion that is not heard as much as I feel it deserves. So thanks for your link !
That said, just a precision though: when dealing with SPAs, there is no such thing as "only in contact us form"... once you've injected some JS, you're in the place until the user leaves or refreshes.
Once your injected code is running, with cookies auth, you will probably be able to perform whatever GET request against the server to steal whichever userdata you want: They'll be kindly authenticated for you by the browser.
At least, with tokens instead of cookies, you get a chance to hide your auth token in a private scope.
But anyway, my point was not to prove that one if better than another :)
Does the server need to know? Cookies. Otherwise, local storage. There's no debate to be had here.
That's the thing. My point is that this is a dogma that I feel is not true when you deal with smaller projects, or with junior developers. You can write broken auth mechanisms even more easily when using cookies compared to storing a token in localStorage.
See my edit (on top of the article for the "server to know" part).
You can write broken auth mechanisms using anything if you don't know what you're doing. It's the reason why we don't trust beginners to write security-related code until they have developed a good understanding of the implications.
Ultimately, if an attacker has gotten to the point of running arbitrary code in your system, be it clientside or serverside, you're screwed either way. This is an all or nothing situation. You can't be somewhat dead or, as my math teacher used to put it, there is no half pregnant (not even the weirdest one of her pet phrases).
So yes, if the server needs to know (anew with every request), a cookie is the right place to put the data. If the server generally doesn't need to know, then local storage.
You've made me realize that this might be another confusion: I'm speaking about SPAs ... for which the only required auth is via Ajax calls (meaning you have control over headers). I'll edit more.
For SPAs it's a bit easier to use localstorage, but I still think cookies are the place to transmit tokens: their whole point is to transmit certain data with every request, so I don't see much of a point in re-inventing the wheel. For XSRF there's specific mechanisms to mitigate it.
What's more, with cookies, the browser keeps track of what goes to which domain, whereas if you manually insert a header into every fetch request, you might by mistake end up sending the token to the wrong domain, if an attacker finds a way to inject an off-domain URL into your application.
You can mitigate this very easily. That's what CSPs are for.
Actually, it's much more easy and reliable than mitigating XSRF, given that you just have to do it once, and it does not require any code... (you can just put the right CSP in your CDN config)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.