Overview
CVE-2007-0405 describes a sensitive data exposure risk in Django 0.95 where the LazyUser class in AuthenticationMiddleware caches the username across requests. An authenticated remote user could exploit this to gain the privileges of another user. The vulnerability arises from a class-level or module-level cache that persists user identity beyond a single request, enabling cross-request leakage of credentials. This class of bug is particularly dangerous in multi-tenant deployments or long-lived worker processes where user identity can be inadvertently reused across requests. The patch and subsequent Django releases fix the caching behavior to ensure per-request evaluation of user identity, eliminating cross-request leakage. This guide references the CVE-2007-0405 details to illustrate how a seemingly minor optimization (lazy evaluation with caching) can become a severe security hole if the cached state is shared across requests.
Affected Versions
Django 0.95 (and earlier)
Code Fix Example
Django API Security Remediation
### Vulnerable pattern (shared cache across requests) and the fix (per-request cache) side-by-side
# Vulnerable pattern (class-level cache across requests)
class LazyUserV1:
_cached_username = None # shared across all requests (class-level cache)
def __init__(self, request):
self.request = request
@property
def username(self):
if LazyUserV1._cached_username is None:
# cache persists across requests
LazyUserV1._cached_username = self.request.session.get('username')
return LazyUserV1._cached_username
# Middleware wiring (vulnerable)
def vulnerable_middleware(request):
request.user = LazyUserV1(request)
return request
# Fixed pattern (per-request cache, no cross-request leakage)
class LazyUserV2:
def __init__(self, request):
self.request = request
self._cached_username = None
@property
def username(self):
if self._cached_username is None:
self._cached_username = self.request.session.get('username')
return self._cached_username
# Middleware wiring (fixed)
def fixed_middleware(request):
request.user = LazyUserV2(request)
return request
# Example usage (demonstrating both patterns side-by-side)
if __name__ == '__main__':
class MockRequest:
def __init__(self, username=None):
self.session = {'username': username} if username else {}
# Simulate two sequential requests: first sets Alice, second attempts Bob - vulnerable path would leak Alice
r1 = MockRequest('alice')
r2 = MockRequest('bob')
v1 = vulnerable_middleware(r1).user
print('Vulnerable username (should be alice):', v1.username)
v2 = vulnerable_middleware(r2).username
print('Vulnerable username for second request (may leak):', v2.username)
f1 = fixed_middleware(r1).username
print('Fixed username for first request (alice):', f1)
f2 = fixed_middleware(r2).username
print('Fixed username for second request (bob):', f2)