Sessions are how your application remembers who a user is between requests. Get session management wrong and an attacker can impersonate any user. This lesson covers session tokens, storage, lifecycle, and the common attacks.
How sessions work
HTTP is stateless. Every request is independent. Sessions add state by associating a token with a server-side record of the user's identity and context.
The typical flow:
- User authenticates (username/password, OAuth, passkey, etc.)
- Server creates a session record and generates a unique session ID
- Server sends the session ID to the client (usually as a cookie)
- Client sends the session ID with every subsequent request
- Server looks up the session ID, identifies the user, and processes the request
The session ID is the key to the kingdom. Anyone who possesses it can act as that user.
Session token requirements
A session token must be:
- Unpredictable. Generated using a cryptographically secure random number generator. Not sequential, not derived from the username, not based on the timestamp.
- Sufficiently long. At least 128 bits of entropy (32 hex characters or more). This makes brute-force guessing infeasible.
- Unique. No two active sessions should share a token.
Most frameworks handle token generation correctly if you use their built-in session management. Do not roll your own unless you have a specific reason and the expertise to do it safely.
Cookie configuration
If you are using cookies to store session tokens (and you should for web applications), configure them correctly:
Set-Cookie: session_id=abc123;
HttpOnly;
Secure;
SameSite=Lax;
Path=/;
Max-Age=3600
| Flag | Why it matters |
|---|---|
HttpOnly | JavaScript cannot read the cookie. Prevents session theft via XSS. |
Secure | Cookie is only sent over HTTPS. Prevents theft via network sniffing. |
SameSite=Lax | Cookie is not sent with cross-site form submissions. Mitigates CSRF. Strict is more restrictive but breaks some legitimate flows (e.g., following a link from an email). |
Path=/ | Cookie is sent for all paths on the domain. |
Max-Age | Sets the cookie lifetime. Align with your session expiry. |
Session lifecycle
Creation
Create a new session only after successful authentication. Never reuse a session from before authentication — this prevents session fixation attacks.
Expiry
Sessions should expire after a defined period of inactivity (idle timeout) and an absolute duration regardless of activity (absolute timeout).
| Type | Typical value | Purpose |
|---|---|---|
| Idle timeout | 15–30 minutes | Protects unattended browsers |
| Absolute timeout | 8–24 hours | Limits the window of token compromise |
For sensitive operations (banking, admin panels), shorter timeouts are appropriate.
Invalidation
When a user logs out, destroy the session on the server side. Do not just delete the cookie — an attacker who captured the token could still use it. The server must reject the token after logout.
# Django
request.session.flush()
# Express.js
req.session.destroy()
Also invalidate sessions when:
- The user changes their password
- The user enables or disables MFA
- An admin revokes access
- A security incident is detected
Regeneration
Generate a new session ID after any privilege change:
- After login (prevents session fixation)
- After elevating privileges (user → admin)
- After re-authentication for sensitive operations
# Django — regenerate session ID
request.session.cycle_key()
Session attacks
Session fixation
The attacker sets the victim's session ID before the victim authenticates. If the application does not regenerate the session ID upon login, the attacker already knows the post-authentication token.
Attack flow:
- Attacker obtains a valid anonymous session ID from the application.
- Attacker tricks the victim into using that session ID (via a crafted link, form, or injected cookie).
- Victim authenticates. The application upgrades the existing session to an authenticated one without changing the ID.
- Attacker uses the original session ID to access the authenticated session.
Defence: Always regenerate the session ID after authentication.
Session hijacking
The attacker steals an active session token. Methods include:
- XSS — JavaScript reads
document.cookieand sends the value to the attacker. Mitigated byHttpOnly. - Network sniffing — token intercepted on an unencrypted connection. Mitigated by
Secureflag and HTTPS everywhere. - Cross-site request — cookie sent with a cross-site form submission. Mitigated by
SameSite. - Client-side malware — browser extensions or compromised devices. Harder to defend against from the server side; binding sessions to IP or device fingerprints can help detect anomalies.
Session replay
An attacker reuses a captured session token at a later time. Short session lifetimes and idle timeouts limit the window of exposure. For critical actions, require re-authentication (step-up authentication).
Where to store session state
Server-side store (recommended)
Session data lives in the server (database, Redis, Memcached). The client only holds the session ID. This gives you full control over session lifecycle, size, and security.
Signed cookies (acceptable for small data)
Some frameworks (e.g., Rails, Django with cookie backend) store session data in the cookie itself, signed with a server secret. The data is visible to the client (base64 encoded) but cannot be tampered with.
Limitations:
- Cookie size limits (4 KB)
- Data is visible to the client — do not store sensitive values
- Revocation requires additional infrastructure (cookie signing key rotation invalidates all sessions)
JWTs as session tokens (use with caution)
JWTs stored in cookies or Authorization headers can act as sessions, but they introduce complications:
- No built-in revocation without server-side state (defeating the "stateless" benefit)
- If stored in
localStorage, they are accessible to XSS - Larger payload size increases every request size
If you use JWTs, pair them with short expiry (5–15 minutes) and a server-side refresh token mechanism.
Concurrent session control
Decide whether users can have multiple active sessions (logged in on their phone and laptop simultaneously). If yes, provide a way for users to view and revoke their active sessions (the "security" page in most applications that shows active devices).
If no, invalidate the previous session when a new login occurs. This is more restrictive but limits the damage from a compromised token.
Summary
Session management is the bridge between authentication and every subsequent request. Use cryptographically random tokens, configure cookies with HttpOnly, Secure, and SameSite flags, regenerate the session ID after login, enforce expiry timeouts, and destroy sessions completely on logout. Most frameworks handle the basics — your job is to configure them correctly and handle the lifecycle events (login, logout, password change, privilege escalation) properly.
