DEV Community

Phoenix
Phoenix

Posted on

Cookies vs JWT

Cookies are small pieces of data stored in the browser, typically set by the server using the Set-Cookie header.

They can include attributes such as:
Max-Age / Expires β†’ defines how long the cookie is valid
HttpOnly β†’ prevents access from JavaScript (improves security - it cannot be accessed by JS using document.cookie XSS attacks)
Secure β†’ ensures the cookie is only sent over HTTPS
SameSite β†’ controls cross-site request behavior

πŸ” Authentication flow using cookies (server-side sessions)

  1. User logs in with credentials
  2. Server: Creates a session (stored in DB/memory) Generates a session ID Sends it to the browser via:
Set-Cookie: session_id=abc123
Enter fullscreen mode Exit fullscreen mode
  1. **Browser:
    **Stores the cookie

  2. Automatically includes it in future requests:

Cookie: session_id=abc123
Enter fullscreen mode Exit fullscreen mode

Because they are sent with every request (including images/scripts), large cookies can impact performance

🌐 Cookie behavior in requests
For same-origin requests β†’ cookies are sent automatically
For cross-origin requests β†’ must explicitly enable:
Frontend: credentials: "include" or withCredentials: true
Backend: Access-Control-Allow-Credentials: true
⏳ Expiry behavior

Session cookies (no expiry):
Deleted when browser closes
Persistent cookies (with Max-Age / Expires):
Remain after browser restart

⚠️** Important distinction**
Cookie expiry and session expiry are separate:

Cookie expiry β†’ controlled by browser
Session expiry β†’ controlled by server

Even if a cookie is still valid, the user is logged out if the server session has expired.

Key takeaway
Cookie = identifier stored in browser
Session = actual user data stored on server
Cookie is automatically sent with requests (with some cross-origin rules

Challenges
In a distributed system with multiple backend servers behind a load balancer, session-based authentication can be harder to scale because sessions are stored on the server side and creates challenges like session sharing, synchronization, and additional database or cache lookups on every request

If a session is stored on one server and a subsequent request is routed to a different server, that server won’t recognize the session, making the user appear logged out.

To solve this, systems typically use a shared session store like Redis or a database that all servers can access. However, this introduces additional overhead because every request requires a lookup to fetch session data, which adds latency and increases infrastructure complexity.

This is why session-based systems are considered stateful and harder to scale compared to stateless approaches like JWT.

CSRF = Cross-Site Request Forgery

πŸ‘‰ It’s an attack where:

A malicious website tricks your browser into making a request to another site where you are already logged in.

CSRF is an attack where a malicious site tricks a user’s browser into making unintended requests to another site where the user is authenticated. It happens because cookies are automatically sent with requests. CSRF tokens prevent this by adding a secret value that must be validated by the server.

Browsers automatically send cookies with requests

That’s the root cause.

JWT

JWT (JSON Web Token) is a self-contained token that carries user information and proves authentication.

it is compact URL-safe means of representing claims to be transferred between two parties”. A JWT leverages Javascript Object Notation (JSON) to represent these claims, resulting in a small and simple token that is used by protocols such as OpenID Connect 1.0 to represent identity to the application and OAuth 2.0 to represent an access token for API authorization.

Structure:

header.payload.signature
Enter fullscreen mode Exit fullscreen mode

Header β†’ algorithm (e.g., HS256)
Payload β†’ user data (userId, role, exp)
Signature β†’ ensures integrity (not tampered)

πŸ” Signing (Data Integrity)
Server signs the token using a secret key
Signature ensures

  • Token is issued by server
  • Data is not modified

πŸ‘‰ If payload changes β†’ signature becomes invalid

⚠️ JWT is NOT encrypted by default
Payload is base64 encoded (readable)
Anyone can decode it
But cannot modify it (signature protects it)

πŸ‘‰ Do NOT store sensitive data in JWT

πŸ” JWT Authentication Flow

  • User logs in
  • Server generates JWT
  • Client stores token
  • Client sends token in requests:
Authorization: Bearer <token>
Enter fullscreen mode Exit fullscreen mode

Server verifies signature β†’ authenticates user
πŸ“¦ Where to store JWT

  1. localStorage βœ… Pros:
  • Easy to use
  • Full control in frontend ❌ Cons:
  • Vulnerable to XSS attacks (JS can read token)
  • Cookies (HttpOnly) βœ… Pros:
  • Protected from XSS (HttpOnly)
  • Automatically sent with requests ❌ Cons:
  • Vulnerable to CSRF attacks

Storage Risk
localStorage XSS
Cookies CSRF

βœ… Best practice
Store JWT in HttpOnly cookies
Use:
Secure
SameSite

🚨 CSRF (Cross-Site Request Forgery)
🧾 Example
You are logged into bank.com

Attacker site triggers:

<img src="https://bank.com/transfer?amount=1000">
Enter fullscreen mode Exit fullscreen mode

Browser sends:

Cookie: session_id=abc123
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Server thinks request is valid

πŸ›‘οΈ** CSRF Protection**

  1. CSRF Token (core idea)

πŸ‘‰ Add a secret token that attacker cannot access

πŸ” Synchronizer Token Pattern

  • Server stores
  • session β†’ csrf_token
  • Sends token to client
  • Client sends it back
  • Server validates

πŸ” Double Submit Cookie Patter
Step 1: Server sends cookies
session_id=abc123 (HttpOnly)
csrf_token=xyz789 (readable by JS)
Step 2: Client sends request
Cookie: session_id=abc123; csrf_token=xyz789
Header: X-CSRF-Token: xyz789
Step 3: Server validates
cookie csrf_token === header csrf_token
βœ… Why it works
Attacker cannot:
Read cookie
Set custom headers

πŸ‘‰ Request is rejected

  1. SameSite Cookies Set-Cookie: session_id=abc123; SameSite=Strict

πŸ‘‰ Prevents cookies in cross-site requests

πŸ” Access Token vs Refresh Token
🧾 Access Token
Short-lived (e.g., 15 min)
Used for API requests
Authorization: Bearer
πŸ” Refresh Token
Long-lived (e.g., 7 days)
Used to get new access tokens

⚠️ Important Concepts Summary
Integrity vs Confidentiality
Integrity β†’ data not modified (JWT provides this)
Confidentiality β†’ data hidden (JWT does NOT provide by default)
Sessions vs JWT
Session β†’ server stores data (stateful)
JWT β†’ client carries data (stateless)
CSRF vs XSS
Attack Target
CSRF Cookies (auto-sent)
XSS localStorage / JS-accessible data

Top comments (0)