There is a lot of confusion about cookies, sessions, token-based authentication, and JWT.
Today, I want to clarify what people mean when they talk about āJWT vs Cookie, āLocal Storage vs Cookies,ā āSession vs token-based authentication,ā and āBearer token vs Cookieā once and for all.
Hereās a hint ā we should stop comparing JWT and Cookies!
Along the line, Iāll go through what XSS and CSRF attacks are and how to prevent them using token-based authentication with JWT and CSRF tokens.
Letās begin!
Overview
- Terminology Speed Run
- JWT
- Session-based vs Token-based Authentication
- Cookie vs Bearer Tokens
- CSRF Prevention
- Conclusion
- Reference
Terminology Speed Run
To start, itās important to know the differences between some of these terminologies.
Without explicitly stating these, it would be unclear for us to compare things properly.
Client
Our client application. In this context, we are specifically talking about our web browsers, e.g., Firefox, Brave, Chrome, etc.
Server
Computers that are doing all the magic behind the curtains.
Request/Response Headers
HTTP headers. Note that they are case-insensitive.
Cookie
Aka āHTTP cookie,ā āweb cookie,ā or ābrowser cookie.ā
A small piece of information that a server sends back to the client.
Stored in the browserās Cookies storage,Ā cookies are typically used for authentication, personalization, and tracking.
A cookie is received in name-value pairs via theĀ Set-Cookie
Ā response header in a request. With this, your cookie will automatically be kept in the browserās Cookies storage (document.cookie
).
An example of how a Cookie is received. You should never publicly share your JWT! (this JWT is no longer in use).
Cookies withĀ HttpOnly
,Ā Secure
, andĀ SameSite=Strict
Ā flags are more secure.
For example, with theĀ HttpOnly
Ā flag,Ā the cookies are not accessible through JavaScript, thus making it immune to XSS attacks.
With HttpOnly, the cookie is not shown
Read more on MDNĀ and check out what other flags do (they are handy).
XSS Attack
Aka āCross-Site Scriptingā attack.
For context, the web storage (e.g., local storage) is accessible through JavaScript on the same domain. Consequently, web storage is vulnerable to XSS attacks.
In short, XSS is a type of vulnerability where an attacker injects JavaScript that will run on your page.
Basic XSS attacks attempt to inject JavaScript through form inputs, where the attackerĀ puts anĀ alert(localStorage.getItem('your-secret-token'))
Ā into a form toĀ see if it is run by the browser and can be viewed by other users.
If you still donāt get what XSS is, check outĀ this āXSS Explainedā video.
CSRF Attack
Aka āCross-Site Request Forgeryā attack.
Cookies are vulnerable to CSRF attacks. No cookies = no CSRF attacks.
As browsers automatically send Cookies with all requests, CSRF attacks use this to gain authenticated access to a trusted site.Ā Need more?Ā Read CSRF in simple words.
To protect your site against CSRF attacks while using Cookies (withĀ SameSite=None
), check out thisĀ StackOverflow answer.Ā Next, read more aboutĀ CSRF prevention on WikipediaĀ (Wikipedia is generally too dry to my liking, but this is good!).
Still donāt get what CSRF is? Check outĀ this āCSRF Explainedā video.
Cookies Storage
Aka āCookie Jarā or āCookies.āĀ Yup. I can already see why it is confusing for many.
Client-side storage where HTTP cookies are stored.
Hereās an important note:Ā Browsers automatically send cookies (no client-side code needed) along with every request via theĀ cookie
Ā request header.Ā This is exactly why Cookie (storage) is vulnerable to CSRF attacks.
XSS attacks can be prevented when using Cookies storage if the cookie is set with the HttpOnly flag.
To view Cookies: F12 ā āApplicationā ā āStorageā ā āCookiesā
Web Storage
Aka āLocal/Session Storage.ā
Client-side storage. They are typically used to store data in key-value pairs.
Vulnerable to XSS attacks. Hence, not ideal for storing private/sensitive/authentication-related data.
You can get any item on your Local Storage using JavaScript
To view Local Storage items: F12 ā āApplicationā ā āStorageā ā āLocal Storageā / āSession Storageā
sessionStorage
Ā ā data is persistedĀ only for the duration of the page session
localStorage
Ā ā data is persisted evenĀ when the browser is closed and reopened
Cookies (Storage) vs Web Storage
Local/Session Storage | Cookies (Storage) | |
---|---|---|
JavaScript | Accessible through JavaScript on the same domain | Cookies, when used with the HttpOnly cookie flag, are not accessible through JavaScript |
XSS attacks | Vulnerable to XSS attacks | Immune to XSS (with HttpOnly flag) |
CSRF attacks | Immune to CSRF attacks | Vulnerable to CSRF attacks |
Mitigation | Do not store private/sensitive/authentication-related data here | Make use of CSRF tokens or other prevention methods |
JWT
Aka āJSON Web Tokens.ā
Commonly used forĀ authentication and authorization.
JWT is an open standard (RFC 7519). Meaning all JWTs are tokens.
Typically, JWT is storedĀ in Local Storage or Cookies (storage).
Remember,Ā JWT is not encrypted by any means.
Rather,Ā it is encoded in Base64.Ā Try decoding any JWT onĀ jwt.io.
So, why use JWT?
Often used with token-based authentication, horizontal scaling is easier when using JWT.
Why? TheĀ verification of JWT does not require any communication between the servers and databases. In other words,Ā the authentication can be stateless.
Stop comparing JWT and Cookie
Neither JWT nor Cookie are authentication mechanisms on their own.
JWT is simply aĀ token format.
A cookie is anĀ HTTP state management mechanism.
As demonstrated, a web cookie can contain JWT and can be stored within your browserās Cookies storage.
So, we need to stop comparing JWT and Cookie.
Session-based vs Token-based Authentication
Rather, the right question to ask is, āwhat is the difference between token-based authentication and session-based authentication?ā
Token-based | Session-based |
---|---|
Stateless | Stateful |
The authentication state is NOT stored anywhere on the server-side | The authentication state is stored on the server-side (DB) |
Easier to scale horizontally | Harder to scale horizontally |
Commonly uses JWT for authentication | Commonly uses Session ID |
Typically sent to the server via an HTTP Request Authorization Header (e.g. Bearer <token>) . Can use Cookie too |
Usually sent to the server in the Cookie request header |
Harder to revoke a user session | Able to revoke user session with ease |
Cookie vs Bearer Tokens
Now, we know how cookies work. Letās take a stab at the term āBearer tokens.ā Letās assume weāll use JWT as our authentication token from hereon.
What people call a āBearer tokenā is a string (e.g., JWT) that goes into theĀ Authorization
Ā header of any HTTP request. Unlike a browser cookie, it is not automatically stored anywhere, thus making this CSRF impossible.
To make use of a āBearer token,ā weāll need to explicitly store the JWT somewhere in our client (Cookies storage or Local Storage) and add that JWT to our HTTPĀ Authorization
Ā header while making requests.
If your cookie containing a JWT is set with theĀ HttpOnly
Ā flag, retrieving your token from the client side would be impossible with JavaScript.
āWait, how about we use Local Storage then?ā
Remember, using Local Storage makes our JWT vulnerable to XSS. As a result, youāll often hear people advise strongly against storing JWT in Local Storage.
At this point, it may sound like using Cookie to store JWT is our only option. But remember, this makes our website vulnerable to CSRF attacks!
CSRF Prevention
Same-site Cookies
Same-site cookiesĀ can effectively prevent CSRF attacks. Though, it comes with other caveats.Ā Read more here.
What follows assumes that weāre not going to use SameSite cookies.
Common CSRF prevention methods
Leaving JWT behind for a bit, these two are some of the most common CSRF prevention methods:
Check out thisĀ StackOverflow answerĀ for a quick summary of the two approaches above.
Cool. But now comes the question ā how can we do this with JWT?
The modified ācookie-to-header tokenā approach
Honestly, Iām not too sure if this approach has a proper name. I found this approach from this wonderful talk aboutĀ 100% Stateless with JWT (JSON Web Token) by Hubert SablonniĆØre. I highly recommend you check it out. Itās worth the hour.
In short, the modified approach (in my opinion) looks similar to the originalĀ Cookie-to-header token approachĀ except with a few tweaks:
- the anti-CSRF token is returned in a separate response header (e.g.Ā
X-CSRF-Token
) instead of theĀSet-Cookie
Ā response header - we sign and set a JWT on theĀ
Set-Cookie
Ā response header
This implementation can be found onĀ this Cloudflare Worker demoĀ andĀ GitHub.
- The user logs in, and the server signs a JWT withĀ
csrfToken
Ā as part of the JWT claim.
{
"email": "your@email.com",
"exp": 1666798498,
"csrfToken": "1449bd3e-41c2-45cb-a538-73c7ad80ca2c",
"iat": 1666794898
}
The generatedĀ csrfToken
Ā should be unpredictable and unique per-user session.
The JWT would then be stringified into a cookie set into theĀ
Set-Cookie
Ā response header. The randomly generatedĀcsrfToken
Ā , on the other hand, will be set in theĀX-CSRF-Token
Ā response header.With theĀ
Set-Cookie
Ā header present in the response header, our browser would automatically store the JWT in the Cookies (storage). TheĀcsrfToken
Ā present in theĀX-CSRF-Token
Ā header will be extracted and set in the browserās local storage.When a request (e.g., GET /hello) is triggered, our browser will get theĀ
csrfToken
Ā from the local storage.The JWT from the Cookies (storage) and theĀ
csrfToken
Ā retrieved from the local storage will be sent to the server in our request header.The server will verify the JWT and check theĀ
csrfToken
Ā from the request header with theĀcsrfToken
Ā claim inside the JWT to verify if the CSRF Token is valid.
Conclusion
In essence, as long as authentication isnāt automatic (such as in browsers with Cookies), you donāt have to worry about CSRF attacks.
For example, if your application attaches authentication credentials via aĀ Authorization
Ā header, CSRF isn't possible as the browser can't authenticate the request automatically.
Lastly, we need to stop advocating that any of our comparisons is better. That is not how it works. Rather, we should think about the tradeoffs that we are making.
A Demo, perhaps?
The screenshots in this post are taken fromĀ this Cloudflare Worker demoĀ I made. The source code of this demo can be foundĀ on GitHub.
To test a CSRF attack, check out thisĀ little toolĀ (credits:Ā Shrikant).
That aside, I created aĀ branch nameĀ csrf-attack-demo
Ā where you could run that locally to simulate a CSRF attack on our demo site.
Reference
I owe my thanks to all of the following references:
- 100% Stateless with JWT (JSON Web Token) by Hubert SablonniĆØre
- How to Store Session Tokens in a Browser (and the impacts of each)
This article was originally published on jerrynsh.com
Top comments (0)