Introduction
Once a user logs in, most web applications track them using a session — typically a token stored in a cookie that is sent with every subsequent request. If that token is stolen, guessed, or fixed to a known value by an attacker, the attacker can act as that user without ever knowing their password. Session management security is the set of practices that keep session tokens confidential, unpredictable, and short-lived.
Cricket analogy: Once a player is accredited for a tournament, officials track them using a badge number for every gate entry; if that badge is stolen, guessed, or planted on them by someone else beforehand, an impostor can enter as that player, which is why badge numbers must stay confidential, unpredictable, and expire after the tournament.
Explanation
Session cookies should be set with three key attributes. HttpOnly prevents client-side JavaScript from reading the cookie, which blocks attackers from stealing the session token via a cross-site scripting vulnerability even if one exists elsewhere on the site. Secure ensures the cookie is only ever sent over HTTPS, preventing it from being intercepted in plaintext on the network. SameSite restricts whether the cookie is sent along with cross-site requests; setting it to Strict or Lax significantly reduces cross-site request forgery risk by not attaching the session cookie to requests initiated from other origins. Beyond cookie flags, two related attacks matter: session fixation, where an attacker tricks a victim into using a session ID the attacker already knows (for example, via a URL parameter) and then hijacks the session once the victim logs in under that ID; and session hijacking, where an attacker steals a valid session token through network sniffing, XSS, or a leaked log, and reuses it. The standard defense against fixation is to always issue a brand-new session token when a user authenticates or when their privilege level changes (such as logging in, or elevating to an admin role), rather than reusing whatever session ID existed beforehand.
Cricket analogy: A badge that can't be photocopied by a stray photographer (HttpOnly), is only accepted through the official secured entrance not a side gap in the fence (Secure), and isn't honored if presented by someone arriving with an unrelated tour group (SameSite) blocks theft; and issuing a brand-new badge number the moment a player is promoted to captain defeats a saboteur who pre-planted an old badge number.
Example
# BEFORE: session cookie set with no protective flags, session ID reused after login
response.set_cookie("session_id", session_id)
def login(request):
if valid_credentials(request):
# BUG: keeps the same pre-login session_id (fixation risk)
mark_session_authenticated(request.cookies["session_id"])
# AFTER: secure cookie flags, and a fresh session ID issued on login
response.set_cookie(
"session_id",
session_id,
httponly=True, # not readable by JavaScript
secure=True, # only sent over HTTPS
samesite="Strict" # not sent on cross-site requests
)
def login_secure(request):
if valid_credentials(request):
# Regenerate the session token on privilege change to stop fixation
new_session_id = generate_new_session()
invalidate_session(request.cookies.get("session_id"))
set_authenticated_session(new_session_id, request.user)
return new_session_idAnalysis
The three cookie flags each close a different attack surface: HttpOnly stops script-based theft, Secure stops network-based theft, and SameSite stops cross-site request abuse — none of them substitutes for the others, so all three should be set together. Regenerating the session ID at login is what actually defeats fixation: even if an attacker planted a known session ID in the victim's browser before login, that old ID becomes worthless the moment the victim authenticates, because the server issues and starts trusting a different, freshly-generated ID instead. Sessions should also expire after a reasonable period of inactivity and be invalidated server-side on logout, so a stolen token has a limited useful lifetime.
Cricket analogy: HttpOnly stops a saboteur reading the badge number off a photocopy, Secure stops it being read off an open radio channel, and SameSite stops it being honored from an unrelated group — none substitutes for the others; issuing a fresh badge at accreditation makes any pre-planted badge number worthless, and badges expire after the tournament and are voided when a player withdraws.
Key Takeaways
- Set HttpOnly, Secure, and SameSite on every session cookie — each blocks a different theft vector.
- Session fixation is defeated by issuing a new session token on login or any privilege change, not reusing the pre-login ID.
- Session hijacking is mitigated by short expirations, server-side invalidation on logout, and never exposing tokens in URLs or logs.
- Cookie flags and token regeneration are complementary controls, not substitutes for each other.
Practice what you learned
1. Which cookie attribute prevents client-side JavaScript from reading the session cookie?
2. What is the primary purpose of regenerating the session token immediately after login?
3. Which cookie attribute ensures the cookie is only transmitted over HTTPS connections?
4. Setting SameSite=Strict on a session cookie primarily helps defend against which risk?
Was this page helpful?
You May Also Like
Authentication Basics
Learn how systems verify identity through authentication, how it differs from authorization, and the three factor categories used to prove who you are.
Multi-Factor Authentication
Understand how MFA combines multiple distinct authentication factor categories to dramatically reduce the risk of account takeover.
Cross-Site Scripting (XSS)
Stored, reflected, and DOM-based XSS explained, with output encoding and CSP as the core defenses.
TLS/SSL Basics
Understand how the TLS handshake establishes a secure session, and why SSL is the deprecated predecessor to modern TLS.