HIGH session fixationdjango

Session Fixation in Django

How Session Fixation Manifests in Django

Session fixation in Django occurs when an attacker forces a user to use a predetermined session ID, allowing the attacker to hijack the session after authentication. Django's session framework, while robust, can be vulnerable to fixation if not properly configured or if developers rely on insecure patterns.

The most common Django-specific manifestation involves manipulating the SESSION_COOKIE_NAME or using predictable session keys. Consider this vulnerable pattern:

# views.py
from django.shortcuts import render

def login(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        
        # Vulnerable: doesn't regenerate session ID
        user = authenticate(request, username=username, password=password)
        if user is not None:
            login(request, user)
            return redirect('dashboard')
        else:
            return render(request, 'login.html', {'error': 'Invalid credentials'})

This code authenticates users but fails to regenerate the session ID upon login. An attacker could pre-generate a session ID, trick a victim into using it, then hijack the session after successful authentication.

Another Django-specific vulnerability arises from improper use of SESSION_ENGINE. If using file-based sessions without proper cleanup, session files might persist longer than intended, creating fixation opportunities:

# settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.file'
SESSION_FILE_PATH = '/tmp/sessions/'  # Insecure if not properly secured

Middleware ordering also matters in Django. If custom middleware manipulates sessions before Django's authentication middleware, it can create fixation vectors:

# middleware.py
class CustomSessionMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        # Vulnerable: manipulates session before auth
        if 'user_id' in request.session:
            request.user = get_user(request.session['user_id'])
        
        response = self.get_response(request)
        return response

Django's session fixation can also occur through URL parameters when using SESSION_COOKIE_DOMAIN across subdomains without proper validation, allowing attackers to set cookies across related domains.

Django-Specific Detection

Detecting session fixation in Django requires both manual code review and automated scanning. middleBrick's Django-aware scanner specifically targets Django session management patterns to identify fixation vulnerabilities.

middleBrick scans for these Django-specific fixation indicators:

  • Missing session_regenerate_id() calls after authentication
  • Insecure session engine configurations
  • Predictable session key generation patterns
  • Middleware ordering issues that could enable fixation
  • Cross-domain session cookie configurations

Here's how middleBrick identifies these issues in your Django application:

# Scan a Django API endpoint
middlebrick scan https://your-django-app.com/api/login

The scanner examines your Django application's session handling by:

  1. Analyzing the session backend configuration from Django's settings
  2. Checking for proper session ID regeneration after authentication
  3. Testing for predictable session ID patterns
  4. Verifying middleware ordering and session manipulation

For deeper Django-specific analysis, middleBrick can scan your OpenAPI spec alongside the runtime API:

# Scan with OpenAPI spec analysis
middlebrick scan --spec openapi.yaml https://your-django-app.com

This cross-references your Django authentication endpoints with actual runtime behavior, identifying discrepancies that could indicate fixation vulnerabilities.

Manual detection should include reviewing your Django authentication views for proper session handling:

# Vulnerable - missing session regeneration
def login_view(request):
    if request.method == 'POST':
        user = authenticate(request, username=request.POST['username'], 
                                 password=request.POST['password'])
        if user is not None:
            login(request, user)
            return redirect('dashboard')  # Vulnerable: session ID not regenerated
        return render(request, 'login.html')

# Secure - proper session regeneration
def login_view_secure(request):
    if request.method == 'POST':
        user = authenticate(request, username=request.POST['username'], 
                                 password=request.POST['password'])
        if user is not None:
            login(request, user)
            request.session.cycle_key()  # Regenerates session ID
            return redirect('dashboard')
        return render(request, 'login.html')

Django-Specific Remediation

Remediating session fixation in Django requires implementing proper session management patterns and leveraging Django's built-in security features. The most critical fix is regenerating session IDs after authentication.

Here's the secure Django authentication pattern:

from django.contrib.auth import login, authenticate
from django.shortcuts import redirect, render

def login_view(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        
        user = authenticate(request, username=username, password=password)
        if user is not None:
            # Regenerate session ID to prevent fixation
            request.session.cycle_key()
            
            login(request, user)
            return redirect('dashboard')
        return render(request, 'login.html', {'error': 'Invalid credentials'})

For Django's session configuration, use these secure settings:

# settings.py
# Use secure session engine
SESSION_ENGINE = 'django.contrib.sessions.backends.db'

# Set secure cookie flags
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'  # or 'Strict' for maximum security

# Set proper session timeout
SESSION_COOKIE_AGE = 1209600  # 14 days in seconds
SESSION_EXPIRE_AT_BROWSER_CLOSE = False

# Use cryptographically secure session IDs
SESSION_RANDOM_KEY_LENGTH = 32

For Django applications using middleware, ensure proper ordering:

# settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',  # Must come before auth
    'django.middleware.common.CommonMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    # Your custom middleware
    'myapp.middleware.CustomSessionMiddleware',
]

For logout functionality, ensure complete session invalidation:

from django.contrib.auth import logout

def logout_view(request):
    logout(request)
    # Explicitly clear session data
    request.session.flush()
    return redirect('login')

When using Django's authentication views, you can override them with secure patterns:

from django.contrib.auth.views import LoginView
from django.utils.translation import gettext_lazy as _

class SecureLoginView(LoginView):
    template_name = 'registration/login.html'
    
    def form_valid(self, form):
        # Call parent form_valid to authenticate
        response = super().form_valid(form)
        
        # Regenerate session ID after successful login
        self.request.session.cycle_key()
        
        return response

For Django REST Framework applications, implement session fixation protection in your authentication classes:

from rest_framework.authentication import SessionAuthentication
from rest_framework import exceptions

class SecureSessionAuthentication(SessionAuthentication):
    def authenticate(self, request):
        # Call parent authenticate
        user, auth = super().authenticate(request)
        
        # Regenerate session ID after successful authentication
        if user and auth:
            request.session.cycle_key()
        
        return (user, auth)

Frequently Asked Questions

Does Django automatically protect against session fixation?
No, Django does not automatically regenerate session IDs after authentication. Developers must explicitly call request.session.cycle_key() after successful login to prevent session fixation attacks. This is a common security oversight in Django applications.
How does middleBrick detect session fixation in Django applications?
middleBrick scans Django applications for missing session regeneration after authentication, insecure session configurations, predictable session ID patterns, and middleware ordering issues. It tests authentication endpoints to verify that session IDs change after login and checks for Django-specific fixation patterns that other scanners might miss.