Skip to main content

Overview

Session Fixation is an attack where an attacker forces a victim’s browser to use a specific session identifier known to the attacker before the victim logs in. If the application fails to generate a new session identifier upon successful authentication, the attacker can then use the same, fixated session identifier to impersonate the victim after they have logged in. The attacker essentially “rides along” on the session they forced the user to adopt. 🍪📌 Common Attack Flow:
  1. Attacker obtains a valid session ID from the application (e.g., by visiting the login page).
  2. Attacker tricks the victim into using this specific session ID (e.g., via a phishing link containing the session ID in the URL: http://vulnerable.com/?SESSIONID=attacker_knows_this, or via XSS setting the cookie).
  3. Victim logs into the application using the session ID provided by the attacker.
  4. The application authenticates the user but fails to generate a new session ID.
  5. Attacker uses the known session ID to access the victim’s authenticated session.

Business Impact

Successful session fixation leads directly to session hijacking and account takeover:
  • Account Impersonation: Attackers gain full access to the victim’s account and can perform any action the victim can.
  • Data Theft: Attackers can steal sensitive information accessible within the victim’s session.
  • Unauthorized Transactions: Attackers can perform actions like transferring funds, making purchases, or changing account settings.

Reference Details

CWE ID: CWE-384 OWASP Top 10 (2021): A07:2021 - Identification and Authentication Failures Severity: High

Framework-Specific Analysis and Remediation

This vulnerability occurs when the application’s session management logic fails to invalidate the existing session identifier and issue a new one immediately after successful authentication. Modern web frameworks often handle this automatically as part of their standard login procedures. Key Remediation Principles:
  1. Regenerate Session ID on Login: Crucially, always generate a completely new session identifier and invalidate the old (pre-login) one immediately upon successful user authentication.
  2. Do Not Accept Session IDs from URL Parameters: Configure the framework or server to ignore session identifiers passed as query parameters. Rely only on session cookies.
  3. Use Secure Cookie Attributes: Set HttpOnly, Secure, and SameSite=Strict (or Lax) attributes on session cookies to mitigate other risks like XSS stealing the cookie or CSRF leveraging it (See CWE-1004, CWE-614, CWE-1275).

  • Python
  • Java
  • .NET(C#)
  • PHP
  • Node.js
  • Ruby

Framework Context

Django’s login() function and Flask-Login’s login_user() function typically handle session ID regeneration correctly by default. Vulnerabilities arise in custom login logic or if defaults are overridden.

Vulnerable Scenario 1: Custom Django Login without login()

Manually setting session variables without calling django.contrib.auth.login.
# views/auth.py (Django Custom Login)
from django.shortcuts import redirect
from django.contrib.auth import authenticate

def custom_manual_login(request):
    if request.method == 'POST':
        user = authenticate(username=request.POST['username'], password=request.POST['password'])
        if user is not None:
            # DANGEROUS: Manually setting user ID in session without
            # invalidating the old session ID (fixation).
            request.session['_auth_user_id'] = user.pk
            request.session['_auth_user_backend'] = user.backend
            # Missing: session rotation/invalidation
            return redirect('/dashboard')
        # ... handle failure ...
    # ... render form ...

Vulnerable Scenario 2: Flask Login without Regeneration (Less Common)

While Flask-Login usually handles this, a misconfiguration or a very old version, or manual session handling could be vulnerable.
# app.py (Flask Manual Session)
from flask import session, request, redirect

@app.route('/manual-login', methods=['POST'])
def manual_login():
    username = request.form['username']
    password = request.form['password']
    user = authenticate_user(username, password) # Assume function exists
    if user:
        # DANGEROUS: Setting user in session without regenerating ID.
        # Attacker forces session ID before user hits this.
        session['user_id'] = user.id
        session['logged_in'] = True
        return redirect('/dashboard')
    # ... handle failure ...

Mitigation and Best Practices

  • Django: Always use django.contrib.auth.login(request, user) after authenticating a user. This function automatically rotates the session key.
  • Flask: Use flask_login.login_user(user). Ensure your Flask session configuration is secure (e.g., using Flask-Session with server-side storage and configuring session protection options if available). Modern Flask versions often have basic session rotation.

Secure Code Example

# views/auth.py (Django Secure Login)
from django.contrib.auth import authenticate, login # Import login

def secure_django_login(request):
    if request.method == 'POST':
        user = authenticate(username=request.POST['username'], password=request.POST['password'])
        if user is not None:
            # SECURE: login() handles session regeneration and sets user in session.
            login(request, user)
            return redirect('/dashboard')
        # ... handle failure ...
    # ... render form ...
# app.py (Flask Secure Login with Flask-Login)
from flask_login import login_user # Import login_user

@app.route('/secure-login', methods=['POST'])
def secure_login():
    username = request.form['username']
    password = request.form['password']
    user = authenticate_user(username, password) # Assume returns User object
    if user:
        # SECURE: login_user typically handles session regeneration features.
        # Check Flask-Login docs for specific version behavior if unsure.
        login_user(user)
        return redirect('/dashboard')
    # ... handle failure ...

Testing Strategy

  1. Visit the login page of the application in your browser. Use developer tools to find the current sessionid cookie value.
  2. Copy this session ID.
  3. In a different browser (or incognito window), manually set the session cookie to the copied value (e.g., using browser extensions like Cookie Editor). Alternatively, try passing the session ID as a URL parameter if the application accepts it (?sessionid=...).
  4. Using this second browser (with the fixated session ID), log in successfully.
  5. Go back to the first browser (which still has the original session ID) and refresh the page or navigate to a protected area.
  6. If you are now logged in as the user who authenticated in the second browser, the application is vulnerable to session fixation. A secure application would have invalidated the original session ID upon login in the second browser, leaving the first browser logged out or with a new, unauthenticated session ID.