Session Management Security: HttpOnly Cookies, CSRF, and SameSite Explained
An advanced guide to session management security — covering HttpOnly and Secure cookie flags, CSRF attack mechanics, SameSite policy, and session fixation prevention.
Session management is the part of your authentication system that runs on every single request after the user logs in. It is also where a significant number of web application compromises begin — not through broken cryptography, but through misconfigured cookies, missing CSRF protections, or sessions that never properly expire. These are not exotic vulnerabilities; they are the bread and butter of real-world web security assessments.
This guide covers the full picture of production session management: what the flags do, what they do not do, and how the attacks they prevent actually work.
How Sessions Work and Where They Break
When a user authenticates, the server creates a session record and sends back a session ID in a cookie. Every subsequent request carries that cookie. The server looks up the session, finds the user's identity, and processes the request.
The session ID is the crown jewel. Whoever holds it can impersonate the user. Session management security is fundamentally about protecting that identifier from theft and misuse.
The HttpOnly Flag
Setting HttpOnly on a cookie means the browser will not expose it to JavaScript — not via document.cookie, not via XMLHttpRequest headers that JavaScript can read, not via any browser API.
The attack this prevents: cross-site scripting (XSS) based session theft. If an attacker injects JavaScript into your page — via a stored XSS vulnerability, a malicious ad, or a compromised third-party script — and your session cookie is not HttpOnly, the injected code can read document.cookie and exfiltrate the session ID to an attacker-controlled server.
HttpOnly does not prevent XSS. It prevents XSS from escalating into session theft. The session cookie is still sent on every request — the browser handles that, not JavaScript. The distinction matters: HttpOnly is not a substitute for fixing XSS vulnerabilities.
Set it unconditionally on session cookies:
Set-Cookie: session_id=...; HttpOnly; Secure; SameSite=Lax
The Secure Flag
Secure tells the browser to only send the cookie over HTTPS connections. Without it, if a user connects over HTTP — even accidentally, via a redirect, or on a network that strips HTTPS — the session cookie is transmitted in plaintext.
A passive network attacker on the same WiFi network can capture unencrypted traffic. If session cookies travel over HTTP, the attacker captures them.
Secure should be set on all cookies in production. The only context where it is legitimately absent is local development over HTTP, and even there it is worth running HTTPS locally to catch issues early.
SameSite and CSRF Protection
SameSite is the modern defense against cross-site request forgery. To understand why, you need to understand CSRF.
The CSRF attack: A user is logged into your application. They visit a malicious website. The malicious site makes a request to your application — a form submission, an API call — from within the user's browser. The browser, following normal rules, sends the user's session cookie with the request. Your server sees a valid session and processes the request as if the user intended it.
CSRF works because browsers historically sent cookies on any request to a given domain, regardless of where the request originated. The attacker exploits that behavior.
SameSite changes the rule:
SameSite=Strict: The cookie is never sent on cross-site requests. Maximum protection, but breaks some legitimate cross-site flows — following a link from an email to a page that requires the session, for example.SameSite=Lax: The cookie is sent on top-level navigation (clicking a link) but not on cross-origin sub-requests like embedded images or forms. This is the right default for most applications.SameSite=None: The cookie is sent on all cross-site requests. RequiresSecure. Use only when your legitimate use case requires cross-site cookie sharing (embedded widgets, SSO where the IdP and SP are on different domains).
SameSite=Lax blocks the most common CSRF attack vectors while preserving normal user flows. It is the correct default.
When SameSite is Not Enough
SameSite=Lax does not cover all CSRF scenarios. Specifically:
- Same-site requests from subdomain attacks. If an attacker can inject content on
attacker.yourdomain.com, that request is same-site and the cookie is sent. - Old browsers. Some older browsers do not support
SameSite. For applications that must support them, layer a traditional CSRF token on top. - Mobile app webviews. Cookie SameSite behavior in webviews is not always predictable.
For applications with significant security requirements, layer both: SameSite=Lax as the primary defense, plus a CSRF token (Double Submit Cookie pattern or Synchronizer Token pattern) as a secondary defense.
Session Fixation
Session fixation is less commonly known but worth understanding. In a fixation attack, the attacker sets a known session ID on the victim's browser before the victim logs in — via URL parameter injection, a subdomain cookie set trick, or a misconfigured login endpoint that accepts externally provided session IDs.
When the victim logs in, the server associates the attacker's known session ID with the authenticated user. The attacker, who already knows the session ID, now has an authenticated session.
Prevention: Issue a fresh session ID immediately upon successful authentication. This is sometimes called session regeneration. The old (unauthenticated) session is invalidated, and the new session ID has no value to an attacker who set up the fixation.
Most mature session frameworks do this automatically. If you are using a custom session implementation, add this explicitly.
Session Timeout
Sessions that never expire are persistent risk. A session token captured from a user's device years ago should not still grant access today.
Two types of timeout are needed:
- Idle timeout. Invalidate the session after a period of inactivity (30-60 minutes for most apps, shorter for sensitive operations).
- Absolute timeout. Invalidate the session after a maximum lifetime regardless of activity (8-24 hours for most apps). Forces re-authentication periodically.
Implement both. Idle timeout alone can be defeated by an attacker who makes periodic requests to keep the session alive.
Logout Must Invalidate Server-Side State
This is straightforward but commonly skipped: when a user logs out, the server must delete or invalidate the session. Clearing the cookie on the client alone is not sufficient — any attacker who captured the session ID before logout can still use it.
On logout:
- Delete the session from your session store (Redis, database).
- Send
Set-Cookie: session_id=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Secureto clear the cookie in the browser. - Respond with a redirect to the login page.
Session management security is largely unglamorous — it is cookie flags and timeouts and a correct logout flow. But getting these fundamentals wrong creates the attack surface that leads to account compromise. Every one of these controls is worth the five minutes of configuration it takes to enable.
If you need a security-first session management implementation built into your web application from the ground up, Clixo builds production authentication systems for teams who take it seriously.