Session fixation: an attacker plants a known session ID via XSS, MITM or subdomain injection before login, then reuses it for the authenticated session.
__Host- prefix blocks subdomain deliveryCookie-based session fixation is the delivery mechanism where an attacker uses the browser's cookie storage to plant a pre-chosen session ID before the victim logs in. Unlike URL-parameter fixation — which requires the victim to click a specific crafted link — cookie-based fixation can persist in the browser across multiple page visits. The victim sees no indication that their session ID was externally planted.
The attack combines two weaknesses: the delivery mechanism (how the attacker sets the cookie) and the core vulnerability (server does not regenerate the session ID on authentication). Hardening cookies with HttpOnly, Secure, SameSite, and the __Host- prefix closes most delivery vectors. But without session regeneration on login, any delivery mechanism that bypasses these controls enables full session fixation. Both defenses are independently required.
Cookie-based fixation maps to CWE-384 (Session Fixation) with CWE-79 (XSS) or CWE-614 (Missing Secure Flag) as contributing weaknesses. Under OWASP A07:2025, missing cookie flags are independently reportable findings even when the session is rotated on login, because they represent a defense-in-depth failure.
sequenceDiagram
participant A as Attacker
participant V as Victim Browser
participant S as Server
Note over A,V: Delivery Phase (one of three vectors)
A->>V: "XSS document.cookie = session=KNOWN_ID path=/"
Note over V: OR MITM injects Set-Cookie on HTTP response
Note over V: OR Subdomain sets Domain=.parent.com cookie
V->>S: "GET /login (Cookie session=KNOWN_ID)"
S-->>V: "200 Login page"
V->>S: "POST /login with valid credentials"
S-->>V: "302 /dashboard (session KNOWN_ID NOT rotated)"
A->>S: "GET /dashboard (Cookie session=KNOWN_ID)"
S-->>A: "200 victim account page"The three primary delivery vectors, in order of exploitability:
XSS delivery — stored or reflected XSS executes document.cookie = "session=ATTACKER_VALUE; path=/". Only works if the session cookie lacks HttpOnly, since document.cookie cannot write HttpOnly cookies.
MITM delivery — attacker on the network path intercepts an HTTP response and injects a Set-Cookie header. The Secure flag prevents this on HTTPS, but mixed-content pages or HTTP downgrade attacks remove that protection.
Subdomain delivery (cookie tossing) — a compromised subdomain issues Set-Cookie: session=KNOWN; Domain=.example.com; Path=/. The browser accepts the cookie and sends it to the parent domain on all future requests.
-- Subdomain tossing delivery --
HTTP/1.1 200 OK
Host: abandoned.example.com
Set-Cookie: session=ATTACKER_KNOWN; Domain=.example.com; Path=/; Max-Age=86400
-- Victim visits main app --
GET /login HTTP/1.1
Host: example.com
Cookie: session=ATTACKER_KNOWN
POST /login HTTP/1.1
Cookie: session=ATTACKER_KNOWN
email=victim%40example.com&password=correct
HTTP/1.1 302 Found
Location: /dashboard
-- No Set-Cookie: session ID not rotated --
-- Attacker sends: --
GET /dashboard HTTP/1.1
Cookie: session=ATTACKER_KNOWN
HTTP/1.1 200 OK -- victim's account --| Delivery Vector | Prerequisite | Cookie Flag Defense | Example |
|---|---|---|---|
| XSS cookie injection | Stored/reflected XSS, non-HttpOnly session | HttpOnly | HackerOne #1629543 (GitLab) |
| MITM on HTTP | Network position, non-Secure cookie | Secure + HSTS | Public WiFi or TLS inspection proxy |
| Subdomain cookie tossing | Subdomain takeover, leading-dot Domain= | __Host- prefix | HackerOne #1234231 |
| CORS-based injection | CORS misconfiguration allows credentialed Set-Cookie | SameSite=Strict | OWASP OTG-SESS-003 |
| Email HTML injection | HTML email with <meta> or <link> cookie injection | Secure + SameSite | Theoretical, documented |
XSS delivery is the most common. An attacker who finds stored XSS on any page of the application — even a low-value feature like a comments section — can plant the session cookie for every user who views that page. The cookie persists until the victim logs in and (if the server is vulnerable) beyond.
MITM delivery exploits missing Secure flags. Enterprise networks with SSL inspection proxies, public WiFi, or applications that serve mixed HTTP/HTTPS content all create MITM delivery opportunities. Even if the main login page is HTTPS, a single HTTP resource on a related domain can be intercepted to inject cookies.
Subdomain tossing is particularly dangerous because it requires no XSS — only DNS-level compromise of any subdomain in the eTLD+1. Bug bounty programs consistently find abandoned subdomains pointing to decommissioned cloud services.
HackerOne #1629543 — GitLab OAuth2 Session Fixation
GitLab did not rotate the session ID after OAuth2 code redemption. A subdomain XSS vulnerability allowed the attacker to set a known session cookie for the parent gitlab.com domain (cookie tossing). When the victim authenticated via Google SSO, the unrotated session became fully authenticated. The severity was escalated from Medium to High because admin accounts were reachable through this chain.
HackerOne #1234231 — Auth0 Tenant Subdomain Cookie Tossing
An application using Auth0 with Domain=.app.example.com cookie configuration was exposed when an attacker discovered an abandoned subdomain pointing to a decommissioned service. From the hijacked subdomain, the attacker issued Set-Cookie: auth_session=KNOWN; Domain=.app.example.com. When users authenticated on the main tenant, the non-rotated session gave the attacker full account access.
Set-Cookie response header — verify HttpOnly, Secure, SameSite, and Domain attributes.document.cookie = "session=test" — if the assignment succeeds and the cookie is created, the cookie lacks HttpOnly.Domain=. (leading dot) in the Set-Cookie header — flag for subdomain tossing risk.subfinder -d example.com and test each for subdomain takeover candidates.curl -I https://target.com — missing Strict-Transport-Security header enables HTTP downgrade for MITM delivery.# nuclei cookie attribute checks
nuclei -u https://target.com -t http/misconfiguration/cookies/cookie-without-httponly.yaml
nuclei -u https://target.com -t http/misconfiguration/cookies/cookie-without-secure.yaml
nuclei -u https://target.com -t http/misconfiguration/cookies/cookie-without-samesite.yaml
# subzy for subdomain takeover candidates
subzy run --targets subdomains.txt --concurrency 100BreachVex detects cookie-based session fixation by: auditing all Set-Cookie attributes per OWASP ASVS V3.4 requirements, attempting pre-auth session cookie replay through the login endpoint, and comparing post-auth session values. A matching pre/post value triggers CWE-384 HIGH. Missing HttpOnly, Secure, and SameSite are reported as separate CWE-614/1004/1275 findings.
<?php
// PHP — full remediation
session_start();
if (authenticate($_POST['email'], $_POST['password'])) {
session_regenerate_id(true); // true: delete old session file from storage
$_SESSION['user_id'] = $user_id;
}
// php.ini — block all non-cookie session delivery
session.use_only_cookies = 1
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = "Strict"
session.use_strict_mode = 1# FastAPI — __Host- prefix session cookie (strongest protection)
response.set_cookie(
key="__Host-session",
value=new_session_token,
httponly=True,
secure=True,
samesite="strict",
max_age=3600,
path="/",
# domain= MUST be omitted for __Host- prefix to be valid
)// Next.js (API route) — iron-session with __Host- prefix
import { getIronSession } from 'iron-session';
const sessionOptions = {
cookieName: '__Host-session',
password: process.env.SESSION_SECRET!,
cookieOptions: {
secure: true,
httpOnly: true,
sameSite: 'strict' as const,
maxAge: 3600,
path: '/',
// domain: omitted — required for __Host- prefix
},
};Strict-Transport-Security: max-age=63072000; includeSubDomains; preloadincludeSubDomains prevents HTTP downgrade on any subdomain, blocking the MITM cookie injection vector. Without includeSubDomains, a MITM on static.example.com over HTTP can inject cookies for example.com.
The __Host- cookie prefix eliminates subdomain tossing, MITM HTTP delivery, and Domain-scoped cookie injection in a single configuration change. It is the strongest available browser-enforced protection against cookie-based fixation delivery. The only caveat: applications served from a non-root path (e.g., /app/) cannot use __Host- because it requires Path=/.
Cookie-based session fixation occurs when an attacker plants a session cookie with a known value in the victim's browser before the victim logs in. This is done via XSS, MITM on non-HTTPS pages, or subdomain cookie injection. If the application does not regenerate the session ID on login, the attacker's known cookie value becomes an authenticated session credential.
A stored XSS vulnerability on any page in the same origin allows an attacker to inject JavaScript that sets a session cookie. When the victim visits the infected page before logging in, their browser now has the attacker-chosen session ID. If the server does not rotate this ID on authentication, fixation is complete. Note: HttpOnly prevents JavaScript from reading cookies but does NOT prevent JavaScript from setting cookies that lack HttpOnly.
HttpOnly prevents JavaScript from setting the session cookie via document.cookie. Secure prevents delivery over HTTP (MITM on cleartext). SameSite=Strict prevents cross-site cookie delivery. The __Host- prefix prevents subdomain delivery by requiring the cookie to be set only from the exact host. All four together provide defense-in-depth against cookie-based delivery vectors.
The __Host- prefix (RFC 6265bis) enforces: Secure flag required, no Domain attribute (host-binding), Path=/ required. Browsers reject Set-Cookie headers for __Host- cookies that violate any constraint. A cookie named __Host-session cannot be set by sub.example.com for example.com — eliminating subdomain fixation delivery. Spring Security 5.8+, Rack 3+, and Django 4.2+ support this prefix natively.
Yes, on non-HTTPS connections or pages with mixed content. The attacker intercepts the HTTP response and injects Set-Cookie: session=ATTACKER_VALUE. The Secure flag prevents this because cookies with Secure are only sent and set over HTTPS. HSTS (Strict-Transport-Security: includeSubDomains) prevents the initial HTTP connection that enables the MITM.
Cookie tossing is a technique where a subdomain sets a cookie with Domain=.example.com, causing the browser to send that cookie to the parent domain. If the attacker controls any subdomain through takeover, they can toss a known session cookie to the main application's login page, enabling session fixation. The __Host- prefix prevents this by forbidding the Domain attribute.
Spring Security's ChangeSessionIdAuthenticationStrategy (default since 3.2) calls HttpServletRequest.changeSessionId() on successful authentication — replacing the session ID atomically. Applications that override this with sessionManagement().sessionFixation().none() disable the protection. The correct configuration is the default: sessionManagement().sessionFixation().changeSessionId().
The cookie travels over cleartext HTTP, so anyone on the network path can read it and, on a mixed-content page, inject a Set-Cookie header carrying a value they chose. That is enough to deliver a known session ID before login. Enforcing Secure on every session cookie and sending HSTS with includeSubDomains closes the delivery vector; only session regeneration at login fixes the underlying flaw.
No. HttpOnly cookies cannot be set or modified via document.cookie from JavaScript. An attacker needs a different delivery vector: MITM (inject Set-Cookie in HTTP response), subdomain cookie injection (Set-Cookie from subdomain with Domain= attribute), or a server-side vulnerability. HttpOnly is essential but does not protect against network-level or subdomain-level injection.
Cookies with SameSite=None are sent in cross-site requests and can be set by cross-site responses. This is required for legitimate third-party use cases (payment widgets, SSO embeds) but creates a session fixation delivery surface if combined with a session cookie. Session cookies must use SameSite=Strict or at minimum SameSite=Lax. SameSite=None on a session cookie requires explicit justification and additional mitigations.