DEV Community

Cover image for Why Cookie is preferable compared to localStorage when it comes to authentication
Duc Le
Duc Le

Posted on

Why Cookie is preferable compared to localStorage when it comes to authentication

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)
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

or do this with http request:

Set-Cookie: <cookie-name>=<cookie-value>
Enter fullscreen mode Exit fullscreen mode

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.

Latest comments (19)

Collapse
 
fruntend profile image
fruntend

Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍

Collapse
 
moofoo profile image
Nathan Cook

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.

Collapse
 
marcroig profile image
Marc

Nice article, thank you!

Collapse
 
jared201 profile image
Jared Odulio

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.

Collapse
 
pilcrowonpaper profile image
pilcrowOnPaper

The benefits of using cookies rather than localsotrage are:

  1. Allows you do auth checks on server render
  2. Tokens can't be stolen - this doesn't mean the attacker can't do actions on behalf of the user, but the attack window is limited to the session since they store the stolen token.

To prevent CSRF, you can also check for the origin header, though as with same-site, it only works on modern browsers.

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
archnemesys profile image
Gonen R • Edited

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).

Collapse
 
szalonna profile image
Joe

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?

Collapse
 
rezarahmati profile image
Reza Rahmati

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

Collapse
 
primo profile image
Primo

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!

Collapse
 
talr98 profile image
Tal Rofe

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:

// highlight-start
const userPickedImageUrl =
  'https://some-invalid-url.com/no-image!jpg" onerror="fetch("https://localhost:3000/buy-product?prodid=abc", { credentials: "include", method: "POST" })'
// highlight-end

const contentWithUserInput = `
  <img src="${userPickedImageUrl}">
`

outputElement.innerHTML = contentWithUserInput
Enter fullscreen mode Exit fullscreen mode

Read more here: academind.com/tutorials/localstora...

Collapse
 
brense profile image
Rense Bakker

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

Collapse
 
mjvmroz profile image
Michael Mroz

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

Collapse
 
googoo_noonoo profile image
Googoo Noonoo

Where is the standard JWT workflow, which protects against CSRF, defined?

Collapse
 
brense profile image
Rense Bakker

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.

Thread Thread
 
googoo_noonoo profile image
Googoo Noonoo

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.

Thread Thread
 
brense profile image
Rense Bakker

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.

Collapse
 
leduc1901 profile image
Duc Le

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

Collapse
 
nikki_eke profile image
Nikki Eke

I learnt something new today, thanks for sharing.