Authentication on the Web: Sessions vs JWTs

Understand how users stay logged in — and how we as pentesters can take advantage of it.

Authentication is the process of proving who you are. Every time you log in to a website — with a username and password — you’re authenticating.

But logging in is just step one.

What really matters is what happens after you log in:
How does the site remember you for the next request, the next page, or the next day?

That’s where sessions and tokens come in.

Imagine walking into a nightclub.

  • With Sessions, the bouncer gives you a wristband. He knows you’re on the guest list and checks that wristband every time you move.
  • With Tokens, you get a digital badge that proves you’re VIP. You show it to every staff member, and they verify it — the bouncer doesn’t have to remember anything about you.
  1. You POST your username and password.
  2. If correct, the server creates a session — like a file or object stored in memory.
  3. The server sends a session ID to your browser as a cookie: Set-Cookie: sessionid=abc123;
  4. Your browser includes that cookie in every future request: Cookie: sessionid=abc123

The server uses that session ID to check if you’re logged in.

POST /login HTTP/1.1
Content-Type: application/x-www-form-urlencoded

username=admin&password=letmein
HTTP/1.1 200 OK
Set-Cookie: sessionid=abc123;

Every future request will include:

GET /profile HTTP/1.1
Cookie: sessionid=abc123
  • Session Fixation (reuse a known session ID)
  • Session Hijacking (steal session ID via XSS)
  • No session timeout
  • No Secure or HttpOnly flags on cookies
  1. You POST your login info to /login
  2. Server returns a token (usually a JWT)
  3. You store the token (in browser storage or memory)
  4. For every request, you include: Authorization: Bearer <your_token_here>

No session data is stored on the server. Everything the server needs is inside the token itself.

A JSON Web Token (JWT) is a compact, base64-encoded string with 3 parts:

<header>.<payload>.<signature>

Example payload:

{
  "username": "admin",
  "admin": true,
  "exp": 1730000000
}

The signature is used to ensure the token wasn’t tampered with.

curl -X POST https://site.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin", "password":"letmein"}'
{
  "status": "success",
  "auth_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6..."
}

You now include this token on every request:

GET /api/users
Authorization: Bearer eyJhbGciOi...
  • Stateless (server doesn’t need to store sessions)
  • Ideal for mobile apps and APIs
  • Can include custom claims (e.g., "admin": true)
  • Scalable across microservices
  • Token can’t be revoked easily (unlike sessions)
  • Stored in browser = risk of XSS theft
  • If signed with a weak secret or misconfigured (alg: none) → easy to forge
  • Bigger payloads = more exposure if intercepted
FeatureSessionsJWTs
Stored where?On the serverOn the client
Stateless?❌ No✅ Yes
Sent viaCookiesAuthorization header
Revocable?✅ Yes❌ Not easily
Best forWebsitesAPIs, mobile, SPA apps
Security risksCookie hijackingToken forgery, XSS theft

As a pentester, knowing how the app handles authentication lets you plan your attacks:

  • Look at the cookie: can it be stolen, reused, manipulated?
  • Is it missing flags like Secure, HttpOnly, SameSite?
  • Can you force a session ID via Fixation?
  • Decode the JWT: what’s in the payload?
  • Is it signed with a weak secret? Can you crack it?
  • Does the app blindly trust the token without rechecking backend permissions?
SignWhat It Means
Set-Cookie: in responseLikely session-based
Authorization: Bearer in requestsLikely JWT/token-based
Long base64-looking string in localStorageProbably a JWT
Burp proxy shows consistent cookie valuesSessions
Reuse of static tokens across requestsJWTs or API keys
ToolUse
Burp SuiteSee headers, cookies, tokens
jwt_tool.pyDecode, fuzz, and attack JWTs
PostmanTest with headers, cookies, tokens
curlSend raw requests manually
httpieEasier-to-read version of curl

✅ Watch what headers go in and out of login requests
✅ Practice stealing/modifying cookies vs tokens
✅ Decode JWTs and inspect their claims
✅ Test endpoints both with and without tokens
✅ Try using expired or modified tokens — how does the app react?

Authentication is one of the most important aspects of modern web apps — and one of the most abused.

Once you know the difference between Sessions and JWTs, you know where to look, what to manipulate, and how to go from login to full compromise.

Scroll to Top