Survey of all session fixation variants — URL parameter, cookie injection, subdomain, meta refresh, and non-rotation — with a decision tree for selecting the right test and fix.
TL;DR
HttpOnly+Secure+SameSite=Strict+__Host- prefixSession fixation (CWE-384) is a class of authentication vulnerabilities where the attacker controls the session identifier value before the victim authenticates. The server's failure to issue a fresh session ID at the authentication boundary transforms an anonymous session token into an authenticated one — using a value the attacker knows.
The five variants documented below differ only in delivery mechanism. All share the same fundamental flaw and the same fundamental fix. Understanding which variant is applicable to a given target depends on the application's session delivery mechanism (cookies vs. URL parameters), its HTML rendering surface (user-controlled content), and its subdomain attack surface.
The vulnerability class has been continuously present in OWASP Top 10 since the first edition, currently under A07:2021 (Identification and Authentication Failures) alongside other session management failures. OWASP ASVS v5 V3.2.1 requires session regeneration at Level 1 — the minimum baseline for all applications, regardless of risk profile. Despite this, CVE-2024-12798 (Apache Tomcat), CVE-2024-13059 (Moodle), CVE-2024-47812 (Casdoor), and CVE-2024-42346 (Portainer) all demonstrated session fixation in major production software in 2024 alone.
Does the application accept session IDs from URL parameters?
├─ YES → URL Parameter Fixation (session-url-param) — test immediately
└─ NO → Continue
Does the session cookie lack HttpOnly / Secure / SameSite / Domain=.?
├─ YES → Cookie-based Fixation (session-cookie) — delivery vectors available
└─ NO → Continue
Does the application render user-controlled HTML?
├─ YES → Meta Refresh Fixation (session-meta-refresh) — check for meta tag filtering
└─ NO → Continue
Does the Domain cookie attribute include subdomains (Domain=.example.com)?
├─ YES → Cross-subdomain Fixation (session-subdomain) — enumerate subdomains
└─ NO → Continue
Is the session ID identical before and after POST /login?
└─ YES → Post-login Non-rotation (session-non-rotation) — most commonSession ID delivered via URL query parameter: ?JSESSIONID=ATTACKER_VALUE, ?PHPSESSID=ATTACKER_VALUE, ?sid=ATTACKER_VALUE. The attacker constructs a link and delivers it to the victim via phishing, QR code, or social engineering.
Affected systems: PHP with session.use_only_cookies = 0, Java servlet containers with URL rewriting enabled, any framework that reads session IDs from request parameters. See URL Parameter Fixation.
Real CVE: CVE-2024-42346 (Portainer 2.21.0 and earlier, CVSS 8.8).
Session cookie planted in the victim's browser via three delivery vectors:
document.cookie = "session=KNOWN_ID" on a page before loginSet-Cookie injection via HTTP response interceptionDomain=.parent.com cookieAffected systems: Any application with missing HttpOnly, Secure, SameSite, or leading-dot Domain=. See Cookie-based Fixation.
Real CVE: CVE-2024-28892 (Netdata missing Secure flag, CVSS 6.5). Real H1: HackerOne #1629543 (GitLab $3,000).
HTML <meta http-equiv="refresh"> tag redirects the victim's browser to the login URL with an attacker-chosen session parameter. Works without JavaScript, bypasses CSP script-src policies, and can be delivered via phishing email or HTML injection in user content.
Affected systems: Applications accepting URL session delivery + rendering user-supplied HTML without stripping <meta> tags. See Meta Refresh Fixation.
Real CVE: CVE-2024-38475 (Apache httpd mod_rewrite injection enabling delivery chain, CVSS 9.1).
Compromised subdomain sets Domain=.parent.com cookie for the main application. An attacker who controls any subdomain (through takeover of an abandoned CNAME) can fix the session for the parent domain.
Affected systems: Applications using Domain=.example.com (leading dot) cookie configuration. The __Host- prefix prevents this entirely. See Cross-subdomain Fixation.
Real H1: HackerOne #1234231 (Auth0 tenant subdomain tossing).
The most prevalent variant. Server reuses the pre-auth session ID after successful authentication. No active delivery required — the attacker observes the pre-auth session ID through logs, Referer headers, URL sharing, or any incidental exposure.
Affected systems: PHP without session_regenerate_id(true), Rails without reset_session, Spring Security with sessionFixation().none(), Java without request.changeSessionId(), Express without req.session.regenerate(). See Post-login Non-rotation.
Real CVE: CVE-2024-13059 (Moodle CVSS 8.8), CVE-2024-47812 (Casdoor CVSS 8.8), CVE-2024-46977 (Gitea CVSS 8.1).
| Variant | Active Delivery Needed | Network Position Needed | Framework Default Risk |
|---|---|---|---|
| URL parameter | Yes (link delivery) | No | PHP, old Java |
| Cookie (XSS) | Yes (stored XSS) | No | Apps with XSS + no HttpOnly |
| Cookie (MITM) | Yes | Yes (on-path) | Apps without HTTPS/HSTS |
| Cookie (subdomain) | Yes (DNS takeover) | No | Apps with Domain=. |
| Meta refresh | Yes (HTML delivery) | No | PHP/Java + user HTML |
| Non-rotation | No (passive) | No | All frameworks if misimplemented |
Post-login non-rotation is the variant that pentests most frequently miss. It requires no exploit — just a differential comparison between two HTTP responses. If the session cookie value is identical before and after POST /login, the application is vulnerable to any attacker who can observe the pre-auth session ID. This is often exploitable via Referer header leakage to third-party analytics scripts that are already present on public pages.
# 1. Test non-rotation (most important)
# Compare Set-Cookie before vs. after POST /login
# Identical value = vulnerable
# 2. Test URL session delivery
curl -v "https://target.com/login?PHPSESSID=FIXATION_TEST" 2>&1 | grep "FIXATION_TEST"
curl -v "https://target.com/login?JSESSIONID=FIXATION_TEST" 2>&1 | grep "FIXATION_TEST"
# If FIXATION_TEST appears in Set-Cookie → URL delivery enabled
# 3. Audit cookie flags
curl -I https://target.com/login | grep -i "set-cookie"
# Check for: HttpOnly, Secure, SameSite, absence of Domain=.
# 4. Subdomain enumeration
subfinder -d example.com -o subdomains.txt
subzy run --targets subdomains.txt # check for takeovers
# 5. HTML injection surfaces
# Test rich text fields, name fields, email templates for unfiltered meta tags# Python — session regeneration after login (FastAPI example)
from fastapi import Request, Response
import secrets
@app.post("/login")
async def login(request: Request, response: Response, form: LoginForm):
user = await authenticate(form.email, form.password)
if not user:
raise HTTPException(status_code=401)
# 1. Regenerate session ID
new_token = secrets.token_urlsafe(32)
await session_store.delete(request.state.session_id)
await session_store.set(new_token, {"user_id": user.id})
# 2. Set secure cookie with __Host- prefix
response.set_cookie(
"__Host-session", new_token,
httponly=True, secure=True, samesite="strict",
max_age=3600, path="/"
)
return {"status": "authenticated"}The __Host- cookie prefix is the single strongest preventive control. It prevents URL-based delivery (forces Secure/HTTPS), subdomain tossing (no Domain attribute), and MITM delivery (forces Secure). Combined with session regeneration on login, it covers all five variant delivery vectors. Implement both — regeneration covers the server-side flaw, __Host- covers the delivery surface.
1. URL parameter fixation: session ID delivered via ?JSESSIONID= or ?PHPSESSID= in a link. 2. Cookie-based fixation: session cookie planted via XSS, MITM, or subdomain tossing. 3. Meta refresh fixation: HTML meta refresh redirects victim to login URL with attacker's session ID. 4. Cross-subdomain fixation: compromised subdomain sets Domain=.parent.com cookie. 5. Post-login non-rotation: server reuses the same session ID before and after authentication. All five share the same root cause (CWE-384) and the same fix: regenerate session ID on login.
Post-login non-rotation is by far the most prevalent. It requires no active delivery mechanism — any method of observing the pre-auth session ID (server logs, URL sharing, Referer headers) suffices. URL parameter fixation is the second most common, particularly in legacy PHP applications with session.use_only_cookies=0 and Java servlet applications using URL rewriting. CVE-2024-13059 (Moodle), CVE-2024-47812 (Casdoor), and CVE-2024-42346 (Portainer) are all post-login non-rotation variants.
Regenerating the session ID immediately after successful authentication. This is the only fix that works against all five variants simultaneously. Cookie flags (HttpOnly, Secure, SameSite, __Host-) reduce delivery vectors for cookie-based fixation. Disabling URL sessions (session.use_only_cookies=1) prevents URL-based and meta refresh delivery. But only session regeneration eliminates the fundamental vulnerability — even if fixation is delivered, the new session ID after login will not match the attacker's pre-planted value.
Start with post-login non-rotation (differential test: compare session token before and after login). This is a 5-minute test that catches the most prevalent variant. Then test URL parameter delivery (?PHPSESSID=test, ?JSESSIONID=test). Then audit cookie flags (HttpOnly, Secure, SameSite, Domain). Then check subdomain enumeration for takeover candidates. Meta refresh delivery is tested if HTML injection surfaces are identified.
Yes. Session fixation against an admin user's session provides full admin privileges. HackerOne #1629543 (GitLab, $3,000) demonstrated this: subdomain XSS enabled cookie tossing for admin sessions, which were then fixed via OAuth2 non-rotation. The attack chain (delivery mechanism + non-rotation) is independent of privilege level — it works against any authenticated session.
The base CVSS 3.1 score is typically 8.8 (High): CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N. Network vector (AV:N) because delivery is over the network. Low complexity (AC:L) because fixation is deterministic once delivered. No privileges required (PR:N). User interaction required (UI:R) because the victim must authenticate. Scope unchanged (S:U). High confidentiality and integrity impact (C:H/I:H) because full ATO is achieved.
CWE-384 (Session Fixation) is the primary. Related: CWE-613 (Insufficient Session Expiration), CWE-1004 (Sensitive Cookie Without HttpOnly), CWE-614 (Sensitive Cookie Without Secure Attribute), CWE-1275 (Sensitive Cookie Without SameSite Attribute), CWE-598 (Use of GET Request Method with Sensitive Query Strings). Each cookie attribute weakness is independently reportable even when session regeneration is implemented.
No. For URL parameter fixation, meta refresh, and post-login non-rotation, the attacker is entirely off-path. They plant the session ID in advance (URL parameter fixation, meta refresh) or observe an existing pre-auth ID (non-rotation), then wait for the victim to authenticate. Only MITM-based cookie delivery requires network path access. Most session fixation attacks are fully remote with no network adjacency requirement.
Spring Security (changeSessionId default since 3.2), Devise/Rails (reset_session since 3.1), Django (session rotation on login via SESSION_COOKIE_AGE + manual call required in views), Laravel (Illuminate session regeneration built into Auth::attempt()). PHP vanilla and manual Java servlets require explicit implementation. Express.js with express-session requires calling req.session.regenerate() manually.
No. CSRF (Cross-Site Request Forgery, CWE-352) tricks an authenticated user into executing state-changing requests using their existing authenticated session. Session fixation (CWE-384) establishes the session ID before authentication. CSRF exploits an existing session; fixation creates a predetermined session. The defenses are also different: CSRF tokens prevent CSRF; session regeneration prevents fixation.
Burp Suite Active Scanner performs differential comparison for non-rotation. OWASP ZAP ASVS scan rule 10029 checks V3.2.1. Nuclei has templates for cookie attribute weaknesses. Custom scripts using requests (Python) can test all five variants. BreachVex covers all variants with dedicated session-fixation detection, using pre/post comparison plus a cookie-attribute audit.