Introduction
We know about JWT, or JSON Web Token, as an industry standard RFC 7519 method for representing claims securely between two parties. JWT is very common nowadays. But where should we store them in the front end?
In this article, I will break down 2 common places to store tokens. Cookies and LocalStorage
Comparison
Local Storage
To use localStorage
, just simply call use the localStorage
object
localStorage.setItem("yourTokenName", yourToken)
localStorage.getItem("yourTokenName", yourToken)
Pros:
- Very convenient, don’t need any backend, just pure JavaScript.
- Big Data size, about 5mb.
Cons:
- Vulnerable to XSS attacks. An XSS attack happens when an attacker can can take the access token that you stored in your localStorage because they can run JavaScript on your website.
Cookies
To set cookie
, we can do:
document.cookie = "cookieName=value"
or do this with http request:
Set-Cookie: <cookie-name>=<cookie-value>
Pros:
- If you’re using httpOnly and secure cookies this means that your cookies cannot be accessed using JavaScript so even if an attacker can run JS on your site, they can't read your access token from the cookie.
- Can set expiration date
Cons:
- Only 4kb of storage
Security concerns
XSS Attacks
Like I said above, local storage is vulnerable because it’s easily accessible using JavaScript and an attacker can retrieve your access token. However, while httpOnly
cookies are not accessible using JavaScript, this doesn't mean that by using cookies you are safe from XSS attacks involving your access token.
If an attacker can run JavaScript in your application, they can just send an HTTP request to your server which will automatically include your cookies; It’s just less convenient for the attacker because they can’t read the content of the token although they might don’t have to.
CSRF Attacks
Cross-site request forgery (also known as CSRF) is a web security vulnerability that allows an attacker to induce users to perform actions that they do not intend to perform.
However, this can be mitigated easily using sameSite
flag in your cookie by including an anti-CSRF token.
Conclusion
Cookies still have some vulnerabilities but it’s preferable compared to localStorage whenever possible. Because:
Both localStorage
and cookies
are vulnerable to XSS attacks, but it's harder for the attacker to do the attack when you're using httpOnly cookies.
Cookies are vulnerable to CSRF attacks, but it can be mitigated using sameSite flag and anti-CSRF tokens.
You can still make it work, even if you need to use the Authorization: Bearer header or your JWT is larger than 4KB.
Top comments (19)
If an attacker want to hack your site, they dont really care if they have to spend 1 extra minute to look for cookies with their malicious code... I'm sure they do that by default. Its important to remember that obscurity !== security. The whole point of jwt was to help protect against csrf attacks. By introducing sane standards that the user can follow that prevents these attacks by default. If you deviate from the normal jwt workflow, you get into danger territory. If you're worried about the contents of your jwt being stolen you should either encrypt it, or preferably, ask yourself why you're putting sensitive data in something that gets sent to the client browser. A jwt itself is not sensitive if you follow the normal path. But if you put user passwords in there, thats obviously a problem and something you should never do. XSS should obviously be avoided by not allowing code injection... If your app is open to code injection, there's nothing else you can do...
If someone manages to inject a script into your page, it's guaranteed that they can take action on behalf of your end users for as long as it's there. But that's a significantly better outcome than handing them the ability to exfiltrate secrets which permit them to act as your users for as long as the security tokens they've harvested are valid.
With JWTs, that's their expiry. And unless your users are logging in every day/hour, that's probably a fair way out. Most JWT-based auth implementations have no associated server-side state; that's why protocols like OAuth define a split between refresh and access tokens.
So that could be something like 30 days of irrevocable access that an attacker receives per harvested token because they got a script to execute once. Whereas a secure, http-only cookie can only be leveraged as long as the script is able to execute on the page. Much better.
Yes, XSS vulnerabilities should be avoided. But in the event that a browser or library bug opens you up to a vulnerability, you want the blast radius to be as small as possible. There's a reason for the expression "security comes in layers".
Where is the standard JWT workflow, which protects against CSRF, defined?
JWT was made to stop secret sharing like session IDs or login credentials between client and server. The attackers can see the values in the jwt, but they cannot make changes to it, because they do not know the secret on the server.
But the author isn’t talking about the contents of a JWT being stolen, right? If you have access to the JWT from a CSRF injection, you can perform the malicious actions that the JWT authorises you to do. The author is saying you should use cookies so that the JavaScript interpreter cannot access any authentication data. I don’t think they’re concerned with the specific contents of the JWT.
A CSRF attack is a trust problem on the server-side. The user is tricked into submitting a request they do not intend to make and the server accepts it because the session ID, for example, is trusted and linked to a particular user. CSRF attacks are naive. They dont know anything about the user, not even the session ID, but the attacker knows that a certain request will be accepted by the server (due to trust) aslong as a user previously logged in. A JWT always has to be sent together with the request by the user and the server needs to validate that JWT to confirm it has not been tampered with. In this case the server only trusts the secret that the JWT was encrypted with, not the JWT itself and that secret never leaves the server, effectively preventing CSRF. JWT is still vulnerable to XSS attacks, because in that case the attacker can access anything in the browser of the victim including the valid JWT and they can attach that to their malicious request. The only way to protect against XSS is to prevent it. Make sure no code can be injected by users.
Storing data in localstorage has another issue, if you are using public computer and you close the browser still your data is there, so it would have been better to compare sessionStorage with cookies
Session storage is only accessible for one browser tab, so it's not even close to any of both. Besides, using public computer with normal browser (not incognito for example) window is dangerous!
Sorry for bad wording from me.
What I tried to say is: Cookie is more preferable compared to localStorage.
Because cookies can do most of localStorage functionalities in somewhat better ways.
This article is a port from my medium page, so this is sort of a formatting mistake, you can check out my original post here: levelup.gitconnected.com/why-cooki...
Every time we argue about how to use the JWT token, we get to the point when we agree that we should only use JWT inside of the backend architecture and use standard session management between the client and backend. In this case we have the possibility to control the client as we can revoke a session any time, and still have the performance benefits of signed tokens as we use them only in secure environment. And on the API layer we can have a mapping which creates a short living access token only for authorize a request in between the services. In client perspective it is the same as we pass down a token as the client only attaches the token/session ID to every request.
And yes, you can blocklist any token any time but in this case what is the benefit of using token as you need to check the blocklist every time?
The benefits of using cookies rather than localsotrage are:
To prevent CSRF, you can also check for the
origin
header, though as withsame-site
, it only works on modern browsers.None are secured. Even if you backend is on the same domain of the server serving the frontend, I could use do actions on your behalf, without getting your token stored in the cookie:
Read more here: academind.com/tutorials/localstora...
i only keep flags to the localStorage e.g. subscribed, logged_in and remove these items (not set to false or anything!) when user closes the browser or become inactive. the better we don't share security practices, the worst the hacker can't assume to follow anything.
If you don't protect the cookie against csrf and xss the cookie is still vulnerable and can be used to gain a fresh new access token, and with that token the attacker still has access to the rest of the APIs.
While it is an interesting approach, it does not free you from protecting the refresh token (the cookie).
I learnt something new today, thanks for sharing.
If you’re putting other sensitive data in a cookie that goes beyond the scope of how JWT are typically used, I would recommend using something like Iron Session instead.