Session Management

By Davy Rogers

The session token is the key to the kingdom. Treat it like one.

Once a user logs in, the session token is their identity for the rest of the visit. Whoever holds that token is treated as that user, no questions asked. So everything in this lesson comes down to one idea: make the token hard to steal, hard to guess, and quick to invalidate.

Here's the loop it sits inside:

  1. The user authenticates.
  2. The server creates a session and generates a unique session ID.
  3. The server sends that ID to the client as a cookie.
  4. The client returns the ID on every request.
  5. The server looks it up and knows who's calling.

What the token has to be

  • Unpredictable. Cryptographically random. Never sequential, never derived from the username.
  • Long enough. At least 128 bits, which is 32 hex characters.
  • Unique. No sharing tokens between users or devices.

Configuring the cookie

Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600
FlagWhy it's there
HttpOnlyJavaScript can't read it, so XSS can't steal it
SecureOnly sent over HTTPS
SameSite=LaxMitigates CSRF

Managing the lifecycle

This is where most session bugs actually live, not in the token generation.

Creation. Issue a new session only after a successful login. Reusing a pre-login session is what enables fixation.

Expiry. Two clocks, not one:

TypeValue
Idle timeout15 to 30 minutes
Absolute timeout8 to 24 hours

Invalidation. On logout, destroy the session on the server. Deleting the cookie alone leaves a valid token that still works if someone captured it.

request.session.flush()  # Django
req.session.destroy()    # Express

Also invalidate on password change, MFA change, and admin revocation.

Regeneration. Issue a fresh ID whenever privilege changes, such as at login or when someone elevates to admin.

request.session.cycle_key()  # Django

How sessions get attacked

Fixation. The attacker plants a known session ID on the victim before they log in, then rides the now-authenticated session. Regenerating the ID at login closes it.

Hijacking. Three routes in, each blocked by one of your flags: XSS reading the cookie (blocked by HttpOnly), network sniffing (blocked by Secure plus HTTPS), and cross-site requests (blocked by SameSite).

Replay. A captured token reused later. Short lifetimes shrink the window; re-authentication on critical actions closes it.

Where to keep the state

Server-side is the safe default. Hold the session in a database or Redis and give the client only the ID.

Signed cookies can hold a little data, capped at 4KB, but the data is visible to the user and revoking it means rotating keys.

JWTs can't be revoked without server-side state, and parked in localStorage they're readable by any XSS. If you use them, keep them short-lived and back them with server-side refresh tokens.

When in doubt, keep the state on the server, regenerate the ID at every privilege change, and make sure logout actually means logged out.