Overview
Injection vulnerabilities in Django historically include flaws in the authentication flow where user identity could be hijacked across requests. CVE-2007-0405 describes a bug in Django 0.95\'s LazyUser class within the AuthenticationMiddleware where the current username could be cached globally rather than per-request, allowing a remote authenticated user to gain the privileges of another user. This kind of cross-request state can persist in the process and leak authentication information from one request to the next, creating a route for privilege escalation in a multi-user deployment.
Affected Versions
Django 0.95 and earlier; patched in later releases
Code Fix Example
Django API Security Remediation
import sys
# Minimal simulation objects to illustrate the vulnerability and fix
class User:
def __init__(self, username, is_privileged=False):
self.username = username
self.is_privileged = is_privileged
class Session(dict):
@property
def session_key(self):
return self.get('session_key', None)
class Request:
def __init__(self, user, session_key):
self.user = user
self.session = Session({'session_key': session_key})
# Vulnerable pattern (global cache across requests)
class LazyUserVuln:
_cached_username = None # global across requests (bad)
def __init__(self, request):
self.request = request
@property
def username(self):
if LazyUserVuln._cached_username is not None:
return LazyUserVuln._cached_username
LazyUserVuln._cached_username = self.request.user.username
return LazyUserVuln._cached_username
def vulnerable_demo():
admin = User('admin', True)
guest = User('guest', False)
req1 = Request(admin, 'sessA')
req2 = Request(guest, 'sessB')
u1 = LazyUserVuln(req1).username # caches 'admin'
u2 = LazyUserVuln(req2).username # incorrectly returns 'admin' due to global cache
print('Vulnerable pattern results -> u1:', u1, ' u2:', u2)
# Fixed pattern (per-request cache, not global)
class LazyUserFixed:
def __init__(self, request):
self.request = request
self._cached_username = None
@property
def username(self):
if self._cached_username is not None:
return self._cached_username
self._cached_username = self.request.user.username
return self._cached_username
def fixed_demo():
admin = User('admin', True)
guest = User('guest', False)
req1 = Request(admin, 'sessA')
req2 = Request(guest, 'sessB')
v1 = LazyUserFixed(req1).username # 'admin'
v2 = LazyUserFixed(req2).username # 'guest'
print('Fixed pattern results -> v1:', v1, ' v2:', v2)
if __name__ == '__main__':
vulnerable_demo()
fixed_demo()