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
localStorage, just simply call use the
localStorage.setItem("yourTokenName", yourToken) localStorage.getItem("yourTokenName", yourToken)
- Big Data size, about 5mb.
cookie , we can do:
document.cookie = "cookieName=value"
or do this with http request:
- Can set expiration date
- Only 4kb of storage
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.
Cookies still have some vulnerabilities but it’s preferable compared to localStorage whenever possible. Because:
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 (20)
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.
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!
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?
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...
The benefits of using cookies rather than localsotrage are:
To prevent CSRF, you can also check for the
originheader, though as with
same-site, it only works on modern browsers.
Nice article, but the last sentence didn't make sense to me...
Using the Authorization header means the token is then more accessible to XSS attacks, so I am not sure what "you can still make it work" means without giving up on the premise of your article (using httpOnly cookies)?
Also, I wasn't sure what you were suggesting for JWT's larger than 4KB in that last sentence either?
Sorry for bad wording from me.
What I tried to say is: Cookie is more preferable compared to localStorage.
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...
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.
Nice article, thank you!
Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍