How Sessions Work
HTTP is stateless, so web applications maintain the illusion of a continuous logged-in experience using session identifiers — typically a random token stored in a cookie that the server maps to server-side session data (user ID, roles, expiry). The security of the entire authenticated experience hinges on that token being unpredictable, kept confidential in transit and storage, and properly invalidated at the right times. OWASP's Session Management Cheat Sheet specifies that session IDs must have at least 128 bits of entropy and be generated using a cryptographically secure random number generator, not a predictable counter or timestamp-based value.
Cricket analogy: It's like a match official's wristband granting dugout access for the day — if the wristband's serial number were predictable (e.g., sequential), anyone could forge the next one and walk into the dressing room.
Session Fixation and Hijacking
Session fixation occurs when an attacker sets a known session ID on a victim's browser before login (e.g., via a URL parameter or a subdomain-shared cookie) and waits for the victim to authenticate, at which point the attacker's pre-known session ID becomes a valid, authenticated session. The fix is to always regenerate the session ID immediately after a privilege change — login, logout, or role elevation — rather than reusing the pre-authentication session. Session hijacking, by contrast, involves stealing an already-valid session token, typically via XSS (reading a non-HttpOnly cookie), network sniffing on unencrypted connections, or malware — which is why cookies should always be marked HttpOnly, Secure, and SameSite.
Cricket analogy: Session fixation is like slipping a specific, pre-numbered access wristband onto a VIP guest before they arrive, knowing that once security validates and activates it at the gate, you already hold a duplicate of that exact wristband.
// Express.js with express-session: regenerate session ID on login, secure cookie flags
const session = require('express-session');
app.use(session({
secret: process.env.SESSION_SECRET,
name: 'sid', // avoid the default 'connect.sid' to reduce fingerprinting
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true, // JS cannot read the cookie, mitigates XSS-based theft
secure: true, // only sent over HTTPS
sameSite: 'lax', // mitigates CSRF while allowing top-level navigation
maxAge: 30 * 60 * 1000 // 30-minute idle timeout
}
}));
app.post('/login', async (req, res) => {
const user = await authenticate(req.body.email, req.body.password);
if (!user) return res.status(401).json({ error: 'Invalid credentials' });
// Regenerate session ID to prevent fixation
req.session.regenerate((err) => {
if (err) return res.status(500).end();
req.session.userId = user.id;
req.session.save(() => res.json({ message: 'Logged in' }));
});
});
app.post('/logout', (req, res) => {
req.session.destroy(() => {
res.clearCookie('sid');
res.json({ message: 'Logged out' });
});
});Placing a session token in the URL (e.g., ?sessionid=abc123) is a serious anti-pattern: URLs get logged in browser history, proxy logs, and the Referer header sent to third-party resources, all of which leak the live session token outside the application's control.
Timeout, Invalidation, and Concurrent Sessions
Sessions should have both an idle timeout (expiring after a period of inactivity, commonly 15-30 minutes for sensitive apps) and an absolute timeout (expiring after a fixed duration regardless of activity, e.g., 8-12 hours), because idle timeout alone doesn't protect against a token stolen early in a long session. Server-side session invalidation on logout is essential — merely clearing the client-side cookie without destroying the server-side session record leaves the token valid if an attacker already captured it. For high-security applications, OWASP also recommends binding sessions loosely to characteristics like IP range or user agent, and offering users a 'view active sessions / sign out everywhere' feature to revoke tokens they no longer recognize.
Cricket analogy: It's like a day-night match having both a specific stumps time (absolute timeout) and a rain-delay rule that abandons play if there's no action for too long (idle timeout) — either condition alone isn't enough to properly end play.
When a user changes their password, all other active sessions for that account should be invalidated server-side. This closes the window where a previously stolen session token remains usable even after the user believes they've secured their account.
- Session IDs must be generated with a cryptographically secure random number generator with at least 128 bits of entropy.
- Session fixation is prevented by regenerating the session ID on every privilege change (login, logout, role change).
- Session hijacking is mitigated with HttpOnly, Secure, and SameSite cookie flags.
- Never place session tokens in URLs — they leak via browser history, logs, and Referer headers.
- Use both idle timeout and absolute timeout; either alone leaves a gap in protection.
- Logout must destroy the server-side session record, not just clear the client-side cookie.
- Offer users visibility into active sessions and the ability to revoke them remotely.
Practice what you learned
1. What is the core defense against session fixation attacks?
2. Which cookie attribute prevents JavaScript (and thus XSS payloads) from reading the session cookie?
3. Why is placing a session ID in a URL parameter dangerous?
4. Why is having only an idle timeout insufficient for session security?
Was this page helpful?
You May Also Like
Broken Authentication Risks
Understand how authentication mechanisms fail in practice — from credential stuffing to weak session handling — and how OWASP frames these risks.
Multi-Factor Authentication
Understand the categories of authentication factors, how TOTP and WebAuthn work under the hood, and why MFA dramatically reduces account-takeover risk.
OAuth and OIDC Security Basics
Learn the difference between OAuth 2.0 (authorization) and OpenID Connect (authentication), and the common misconfigurations that lead to token theft and account takeover.