Broken Function Level Authorization

Broken Function Level Authorization in Django [Mar 2026] [CVE-2007-0405]

[Fixed Mar 2026] Updated CVE-2007-0405

Overview

CVE-2007-0405 documents a Broken Function Level Authorization issue in Django 0.95 where the LazyUser class used by the AuthenticationMiddleware cached the user identity across requests. This could enable a remote authenticated user to gain the privileges of another user by reusing a stale user object during authorization checks. The vulnerability arises from caching user identity in a way that persists beyond a single request, allowing improper access decisions if the session or thread handling changes between requests. Patch availability indicates this behavior was corrected in later Django releases, but the root pattern-caching identity across requests-remains a critical risk when implementing authentication and authorization logic. In practice, an attacker could rely on a worker process reusing a cached user object for multiple requests, effectively bypassing per-request authorization decisions. If a session swap or login state changes were not reflected in the cached value, subsequent requests could be treated as the previously cached user, enabling impersonation or escalation of privileges. This is a classic example of broken function level authorization where access controls depend on a stale or incorrect user identity rather than the current authenticated context. Remediation in current Django involves using the built-in AuthenticationMiddleware to set request.user per request without cross-request caching. Upgrading from Django 0.95 to a modern release fixes the underlying behavior by ensuring the user identity is resolved from the session on every request. If you maintain custom middleware, remove any caching of user identity across requests and rely on per-request resolution via Django’s authentication stack or get_user helper to avoid stale identities. Additionally, implement tests that verify request.user reflects the active session after login/logout and that authorization checks are consistently computed from the current user instance. Audit any thread-local or global caches used to cache user identities and ensure they are reset per request and cannot leak identity across requests.

Affected Versions

Django 0.95 (and earlier)

Code Fix Example

Django API Security Remediation
VULNERABLE PATTERN
import threading
from django.utils.deprecation import MiddlewareMixin
from django.contrib.auth.models import User, AnonymousUser

# BAD: caches user across requests in a thread-local cache
_thread_locals = threading.local()

class LazyUser:
    def __init__(self, request):
        self.request = request

    @property
    def user(self):
        if not hasattr(_thread_locals, 'cached_user'):
            user_id = self.request.session.get('_auth_user_id')
            if user_id:
                try:
                    _thread_locals.cached_user = User.objects.get(pk=user_id)
                except User.DoesNotExist:
                    _thread_locals.cached_user = AnonymousUser()
            else:
                _thread_locals.cached_user = AnonymousUser()
        return _thread_locals.cached_user

class VulnerableAuthMiddleware(MiddlewareMixin):
    def process_request(self, request):
        request.user = LazyUser(request).user

# FIX: Do not cache across requests; resolve per request
class FixedAuthMiddleware(MiddlewareMixin):
    def process_request(self, request):
        user_id = request.session.get('_auth_user_id')
        if user_id:
            try:
                request.user = User.objects.get(pk=user_id)
            except User.DoesNotExist:
                request.user = AnonymousUser()
        else:
            request.user = AnonymousUser()

CVE References

Choose which optional cookies to allow. You can change this any time.