CVE-2014-0482
Description
The contrib.auth.middleware.RemoteUserMiddleware middleware in Django before 1.4.14, 1.5.x before 1.5.9, 1.6.x before 1.6.6, and 1.7 before release candidate 3, when using the contrib.auth.backends.RemoteUserBackend backend, allows remote authenticated users to hijack web sessions via vectors related to the REMOTE_USER header.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
DjangoPyPI | < 1.4.14 | 1.4.14 |
DjangoPyPI | >= 1.5, < 1.5.9 | 1.5.9 |
DjangoPyPI | >= 1.6, < 1.6.6 | 1.6.6 |
DjangoPyPI | >= 1.7a1, < 1.7c3 | 1.7c3 |
Affected products
42cpe:2.3:a:djangoproject:django:*:*:*:*:*:*:*:*+ 39 more
- cpe:2.3:a:djangoproject:django:*:*:*:*:*:*:*:*range: <=1.4.13
- cpe:2.3:a:djangoproject:django:1.4:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.1:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.10:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.11:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.12:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.2:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.4:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.5:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.6:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.7:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.8:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.9:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.1:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.2:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.3:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.4:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.5:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.6:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.7:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.8:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5:alpha:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5:beta:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6:-:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6.1:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6.2:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6.3:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6.4:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6.5:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6:beta1:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6:beta2:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6:beta3:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6:beta4:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7:beta1:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7:beta2:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7:beta3:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7:beta4:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7:rc1:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7:rc2:*:*:*:*:*:*
Patches
4dd68f319b365[1.5.x] Fixed #23066 -- Modified RemoteUserMiddleware to logout on REMOTE_USE change.
4 files changed · +56 −8
django/contrib/auth/middleware.py+20 −8 modified@@ -53,21 +53,19 @@ def process_request(self, request): # authenticated remote-user, or return (leaving request.user set to # AnonymousUser by the AuthenticationMiddleware). if request.user.is_authenticated(): - try: - stored_backend = load_backend(request.session.get( - auth.BACKEND_SESSION_KEY, '')) - if isinstance(stored_backend, RemoteUserBackend): - auth.logout(request) - except ImproperlyConfigured as e: - # backend failed to load - auth.logout(request) + self._remove_invalid_user(request) return # If the user is already authenticated and that user is the user we are # getting passed in the headers, then the correct user is already # persisted in the session and we don't need to continue. if request.user.is_authenticated(): if request.user.get_username() == self.clean_username(username, request): return + else: + # An authenticated user is associated with the request, but + # it does not match the authorized user in the header. + self._remove_invalid_user(request) + # We are seeing this user for the first time in this session, attempt # to authenticate the user. user = auth.authenticate(remote_user=username) @@ -89,3 +87,17 @@ def clean_username(self, username, request): except AttributeError: # Backend has no clean_username method. pass return username + + def _remove_invalid_user(self, request): + """ + Removes the current authenticated user in the request which is invalid + but only if the user is authenticated via the RemoteUserBackend. + """ + try: + stored_backend = load_backend(request.session.get(auth.BACKEND_SESSION_KEY, '')) + except ImproperlyConfigured: + # backend failed to load + auth.logout(request) + else: + if isinstance(stored_backend, RemoteUserBackend): + auth.logout(request)
django/contrib/auth/tests/remote_user.py+18 −0 modified@@ -118,6 +118,24 @@ def test_header_disappears(self): response = self.client.get('/remote_user/') self.assertEqual(response.context['user'].username, 'modeluser') + def test_user_switch_forces_new_login(self): + """ + Tests that if the username in the header changes between requests + that the original user is logged out + """ + User.objects.create(username='knownuser') + # Known user authenticates + response = self.client.get('/remote_user/', + **{'REMOTE_USER': self.known_user}) + self.assertEqual(response.context['user'].username, 'knownuser') + # During the session, the REMOTE_USER changes to a different user. + response = self.client.get('/remote_user/', + **{'REMOTE_USER': "newnewuser"}) + # Ensure that the current user is not the prior remote_user + # In backends that create a new user, username is "newnewuser" + # In backends that do not create new users, it is '' (anonymous user) + self.assertNotEqual(response.context['user'].username, 'knownuser') + def tearDown(self): """Restores settings to avoid breaking other tests.""" settings.MIDDLEWARE_CLASSES = self.curr_middleware
docs/releases/1.4.14.txt+9 −0 modified@@ -38,3 +38,12 @@ if a file with the uploaded name already exists. underscore plus a random 7 character alphanumeric string (e.g. ``"_x3a1gho"``), rather than iterating through an underscore followed by a number (e.g. ``"_1"``, ``"_2"``, etc.). + +``RemoteUserMiddleware`` session hijacking +========================================== + +When using the :class:`~django.contrib.auth.middleware.RemoteUserMiddleware` +and the ``RemoteUserBackend``, a change to the ``REMOTE_USER`` header between +requests without an intervening logout could result in the prior user's session +being co-opted by the subsequent user. The middleware now logs the user out on +a failed login attempt.
docs/releases/1.5.9.txt+9 −0 modified@@ -38,3 +38,12 @@ if a file with the uploaded name already exists. underscore plus a random 7 character alphanumeric string (e.g. ``"_x3a1gho"``), rather than iterating through an underscore followed by a number (e.g. ``"_1"``, ``"_2"``, etc.). + +``RemoteUserMiddleware`` session hijacking +========================================== + +When using the :class:`~django.contrib.auth.middleware.RemoteUserMiddleware` +and the ``RemoteUserBackend``, a change to the ``REMOTE_USER`` header between +requests without an intervening logout could result in the prior user's session +being co-opted by the subsequent user. The middleware now logs the user out on +a failed login attempt.
0268b855f9ea[1.6.x] Fixed #23066 -- Modified RemoteUserMiddleware to logout on REMOTE_USE change.
5 files changed · +65 −8
django/contrib/auth/middleware.py+20 −8 modified@@ -53,21 +53,19 @@ def process_request(self, request): # authenticated remote-user, or return (leaving request.user set to # AnonymousUser by the AuthenticationMiddleware). if request.user.is_authenticated(): - try: - stored_backend = load_backend(request.session.get( - auth.BACKEND_SESSION_KEY, '')) - if isinstance(stored_backend, RemoteUserBackend): - auth.logout(request) - except ImproperlyConfigured as e: - # backend failed to load - auth.logout(request) + self._remove_invalid_user(request) return # If the user is already authenticated and that user is the user we are # getting passed in the headers, then the correct user is already # persisted in the session and we don't need to continue. if request.user.is_authenticated(): if request.user.get_username() == self.clean_username(username, request): return + else: + # An authenticated user is associated with the request, but + # it does not match the authorized user in the header. + self._remove_invalid_user(request) + # We are seeing this user for the first time in this session, attempt # to authenticate the user. user = auth.authenticate(remote_user=username) @@ -89,3 +87,17 @@ def clean_username(self, username, request): except AttributeError: # Backend has no clean_username method. pass return username + + def _remove_invalid_user(self, request): + """ + Removes the current authenticated user in the request which is invalid + but only if the user is authenticated via the RemoteUserBackend. + """ + try: + stored_backend = load_backend(request.session.get(auth.BACKEND_SESSION_KEY, '')) + except ImproperlyConfigured: + # backend failed to load + auth.logout(request) + else: + if isinstance(stored_backend, RemoteUserBackend): + auth.logout(request)
django/contrib/auth/tests/test_remote_user.py+18 −0 modified@@ -118,6 +118,24 @@ def test_header_disappears(self): response = self.client.get('/remote_user/') self.assertEqual(response.context['user'].username, 'modeluser') + def test_user_switch_forces_new_login(self): + """ + Tests that if the username in the header changes between requests + that the original user is logged out + """ + User.objects.create(username='knownuser') + # Known user authenticates + response = self.client.get('/remote_user/', + **{'REMOTE_USER': self.known_user}) + self.assertEqual(response.context['user'].username, 'knownuser') + # During the session, the REMOTE_USER changes to a different user. + response = self.client.get('/remote_user/', + **{'REMOTE_USER': "newnewuser"}) + # Ensure that the current user is not the prior remote_user + # In backends that create a new user, username is "newnewuser" + # In backends that do not create new users, it is '' (anonymous user) + self.assertNotEqual(response.context['user'].username, 'knownuser') + def tearDown(self): """Restores settings to avoid breaking other tests.""" settings.MIDDLEWARE_CLASSES = self.curr_middleware
docs/releases/1.4.14.txt+9 −0 modified@@ -38,3 +38,12 @@ if a file with the uploaded name already exists. underscore plus a random 7 character alphanumeric string (e.g. ``"_x3a1gho"``), rather than iterating through an underscore followed by a number (e.g. ``"_1"``, ``"_2"``, etc.). + +``RemoteUserMiddleware`` session hijacking +========================================== + +When using the :class:`~django.contrib.auth.middleware.RemoteUserMiddleware` +and the ``RemoteUserBackend``, a change to the ``REMOTE_USER`` header between +requests without an intervening logout could result in the prior user's session +being co-opted by the subsequent user. The middleware now logs the user out on +a failed login attempt.
docs/releases/1.5.9.txt+9 −0 modified@@ -38,3 +38,12 @@ if a file with the uploaded name already exists. underscore plus a random 7 character alphanumeric string (e.g. ``"_x3a1gho"``), rather than iterating through an underscore followed by a number (e.g. ``"_1"``, ``"_2"``, etc.). + +``RemoteUserMiddleware`` session hijacking +========================================== + +When using the :class:`~django.contrib.auth.middleware.RemoteUserMiddleware` +and the ``RemoteUserBackend``, a change to the ``REMOTE_USER`` header between +requests without an intervening logout could result in the prior user's session +being co-opted by the subsequent user. The middleware now logs the user out on +a failed login attempt.
docs/releases/1.6.6.txt+9 −0 modified@@ -39,6 +39,15 @@ underscore plus a random 7 character alphanumeric string (e.g. ``"_x3a1gho"``), rather than iterating through an underscore followed by a number (e.g. ``"_1"``, ``"_2"``, etc.). +``RemoteUserMiddleware`` session hijacking +========================================== + +When using the :class:`~django.contrib.auth.middleware.RemoteUserMiddleware` +and the ``RemoteUserBackend``, a change to the ``REMOTE_USER`` header between +requests without an intervening logout could result in the prior user's session +being co-opted by the subsequent user. The middleware now logs the user out on +a failed login attempt. + Bugfixes ========
c9e3b9949cd5[1.4.x] Fixed #23066 -- Modified RemoteUserMiddleware to logout on REMOTE_USE change.
3 files changed · +52 −3
django/contrib/auth/middleware.py+25 −3 modified@@ -1,4 +1,5 @@ from django.contrib import auth +from django.contrib.auth.backends import RemoteUserBackend from django.core.exceptions import ImproperlyConfigured from django.utils.functional import SimpleLazyObject @@ -47,16 +48,23 @@ def process_request(self, request): try: username = request.META[self.header] except KeyError: - # If specified header doesn't exist then return (leaving - # request.user set to AnonymousUser by the - # AuthenticationMiddleware). + # If specified header doesn't exist then remove any existing + # authenticated remote-user, or return (leaving request.user set to + # AnonymousUser by the AuthenticationMiddleware). + if request.user.is_authenticated(): + self._remove_invalid_user(request) return # If the user is already authenticated and that user is the user we are # getting passed in the headers, then the correct user is already # persisted in the session and we don't need to continue. if request.user.is_authenticated(): if request.user.username == self.clean_username(username, request): return + else: + # An authenticated user is associated with the request, but + # it does not match the authorized user in the header. + self._remove_invalid_user(request) + # We are seeing this user for the first time in this session, attempt # to authenticate the user. user = auth.authenticate(remote_user=username) @@ -78,3 +86,17 @@ def clean_username(self, username, request): except AttributeError: # Backend has no clean_username method. pass return username + + def _remove_invalid_user(self, request): + """ + Removes the current authenticated user in the request which is invalid + but only if the user is authenticated via the RemoteUserBackend. + """ + try: + stored_backend = auth.load_backend(request.session.get(auth.BACKEND_SESSION_KEY, '')) + except ImproperlyConfigured: + # backend failed to load + auth.logout(request) + else: + if isinstance(stored_backend, RemoteUserBackend): + auth.logout(request)
django/contrib/auth/tests/remote_user.py+18 −0 modified@@ -95,6 +95,24 @@ def test_last_login(self): response = self.client.get('/remote_user/', REMOTE_USER=self.known_user) self.assertEqual(default_login, response.context['user'].last_login) + def test_user_switch_forces_new_login(self): + """ + Tests that if the username in the header changes between requests + that the original user is logged out + """ + User.objects.create(username='knownuser') + # Known user authenticates + response = self.client.get('/remote_user/', + **{'REMOTE_USER': self.known_user}) + self.assertEqual(response.context['user'].username, 'knownuser') + # During the session, the REMOTE_USER changes to a different user. + response = self.client.get('/remote_user/', + **{'REMOTE_USER': "newnewuser"}) + # Ensure that the current user is not the prior remote_user + # In backends that create a new user, username is "newnewuser" + # In backends that do not create new users, it is '' (anonymous user) + self.assertNotEqual(response.context['user'].username, 'knownuser') + def tearDown(self): """Restores settings to avoid breaking other tests.""" settings.MIDDLEWARE_CLASSES = self.curr_middleware
docs/releases/1.4.14.txt+9 −0 modified@@ -38,3 +38,12 @@ if a file with the uploaded name already exists. underscore plus a random 7 character alphanumeric string (e.g. ``"_x3a1gho"``), rather than iterating through an underscore followed by a number (e.g. ``"_1"``, ``"_2"``, etc.). + +``RemoteUserMiddleware`` session hijacking +========================================== + +When using the :class:`~django.contrib.auth.middleware.RemoteUserMiddleware` +and the ``RemoteUserBackend``, a change to the ``REMOTE_USER`` header between +requests without an intervening logout could result in the prior user's session +being co-opted by the subsequent user. The middleware now logs the user out on +a failed login attempt.
5307ce565fbeFixed #23066 -- Modified RemoteUserMiddleware to logout on REMOTE_USER change.
5 files changed · +65 −8
django/contrib/auth/middleware.py+20 −8 modified@@ -76,21 +76,19 @@ def process_request(self, request): # authenticated remote-user, or return (leaving request.user set to # AnonymousUser by the AuthenticationMiddleware). if request.user.is_authenticated(): - try: - stored_backend = load_backend(request.session.get( - auth.BACKEND_SESSION_KEY, '')) - if isinstance(stored_backend, RemoteUserBackend): - auth.logout(request) - except ImportError: - # backend failed to load - auth.logout(request) + self._remove_invalid_user(request) return # If the user is already authenticated and that user is the user we are # getting passed in the headers, then the correct user is already # persisted in the session and we don't need to continue. if request.user.is_authenticated(): if request.user.get_username() == self.clean_username(username, request): return + else: + # An authenticated user is associated with the request, but + # it does not match the authorized user in the header. + self._remove_invalid_user(request) + # We are seeing this user for the first time in this session, attempt # to authenticate the user. user = auth.authenticate(remote_user=username) @@ -112,3 +110,17 @@ def clean_username(self, username, request): except AttributeError: # Backend has no clean_username method. pass return username + + def _remove_invalid_user(self, request): + """ + Removes the current authenticated user in the request which is invalid + but only if the user is authenticated via the RemoteUserBackend. + """ + try: + stored_backend = load_backend(request.session.get(auth.BACKEND_SESSION_KEY, '')) + except ImportError: + # backend failed to load + auth.logout(request) + else: + if isinstance(stored_backend, RemoteUserBackend): + auth.logout(request)
django/contrib/auth/tests/test_remote_user.py+18 −0 modified@@ -125,6 +125,24 @@ def test_header_disappears(self): response = self.client.get('/remote_user/') self.assertEqual(response.context['user'].username, 'modeluser') + def test_user_switch_forces_new_login(self): + """ + Tests that if the username in the header changes between requests + that the original user is logged out + """ + User.objects.create(username='knownuser') + # Known user authenticates + response = self.client.get('/remote_user/', + **{self.header: self.known_user}) + self.assertEqual(response.context['user'].username, 'knownuser') + # During the session, the REMOTE_USER changes to a different user. + response = self.client.get('/remote_user/', + **{self.header: "newnewuser"}) + # Ensure that the current user is not the prior remote_user + # In backends that create a new user, username is "newnewuser" + # In backends that do not create new users, it is '' (anonymous user) + self.assertNotEqual(response.context['user'].username, 'knownuser') + def tearDown(self): """Restores settings to avoid breaking other tests.""" settings.MIDDLEWARE_CLASSES = self.curr_middleware
docs/releases/1.4.14.txt+9 −0 modified@@ -38,3 +38,12 @@ if a file with the uploaded name already exists. underscore plus a random 7 character alphanumeric string (e.g. ``"_x3a1gho"``), rather than iterating through an underscore followed by a number (e.g. ``"_1"``, ``"_2"``, etc.). + +``RemoteUserMiddleware`` session hijacking +========================================== + +When using the :class:`~django.contrib.auth.middleware.RemoteUserMiddleware` +and the ``RemoteUserBackend``, a change to the ``REMOTE_USER`` header between +requests without an intervening logout could result in the prior user's session +being co-opted by the subsequent user. The middleware now logs the user out on +a failed login attempt.
docs/releases/1.5.9.txt+9 −0 modified@@ -38,3 +38,12 @@ if a file with the uploaded name already exists. underscore plus a random 7 character alphanumeric string (e.g. ``"_x3a1gho"``), rather than iterating through an underscore followed by a number (e.g. ``"_1"``, ``"_2"``, etc.). + +``RemoteUserMiddleware`` session hijacking +========================================== + +When using the :class:`~django.contrib.auth.middleware.RemoteUserMiddleware` +and the ``RemoteUserBackend``, a change to the ``REMOTE_USER`` header between +requests without an intervening logout could result in the prior user's session +being co-opted by the subsequent user. The middleware now logs the user out on +a failed login attempt.
docs/releases/1.6.6.txt+9 −0 modified@@ -39,6 +39,15 @@ underscore plus a random 7 character alphanumeric string (e.g. ``"_x3a1gho"``), rather than iterating through an underscore followed by a number (e.g. ``"_1"``, ``"_2"``, etc.). +``RemoteUserMiddleware`` session hijacking +========================================== + +When using the :class:`~django.contrib.auth.middleware.RemoteUserMiddleware` +and the ``RemoteUserBackend``, a change to the ``REMOTE_USER`` header between +requests without an intervening logout could result in the prior user's session +being co-opted by the subsequent user. The middleware now logs the user out on +a failed login attempt. + Bugfixes ========
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
15- github.com/advisories/GHSA-625g-gx8c-xcmgghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2014-0482ghsaADVISORY
- www.djangoproject.com/weblog/2014/aug/20/security/nvdVendor Advisory
- lists.opensuse.org/opensuse-updates/2014-09/msg00023.htmlnvdWEB
- www.debian.org/security/2014/dsa-3010nvdWEB
- github.com/django/django/blob/aa3cb3f37265be37d892e2b391ff023e9caee2a4/docs/releases/1.5.9.txtghsaWEB
- github.com/django/django/commit/0268b855f9eab3377f2821164ef3e66037789e09ghsaWEB
- github.com/django/django/commit/5307ce565fbedb9cc27cbe7c757b41a00438d37cghsaWEB
- github.com/django/django/commit/c9e3b9949cd55f090591fbdc4a114fcb8368b6d9ghsaWEB
- github.com/django/django/commit/dd68f319b365f6cb38c5a6c106faf4f6142d7d88ghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/django/PYSEC-2014-6.yamlghsaWEB
- www.djangoproject.com/weblog/2014/aug/20/securityghsaWEB
- secunia.com/advisories/59782nvd
- secunia.com/advisories/61276nvd
- secunia.com/advisories/61281nvd
News mentions
0No linked articles in our index yet.