CVE-2015-5963
Description
contrib.sessions.middleware.SessionMiddleware in Django 1.8.x before 1.8.4, 1.7.x before 1.7.10, 1.4.x before 1.4.22, and possibly other versions allows remote attackers to cause a denial of service (session store consumption or session record removal) via a large number of requests to contrib.auth.views.logout, which triggers the creation of an empty session record.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
DjangoPyPI | >= 1.8, < 1.8.4 | 1.8.4 |
DjangoPyPI | >= 1.7, < 1.7.10 | 1.7.10 |
DjangoPyPI | >= 1.4, < 1.4.22 | 1.4.22 |
Affected products
43cpe:2.3:a:djangoproject:django:1.4:*:*:*:*:*:*:*+ 38 more
- 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.13:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.14:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.17:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.19:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.2:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.20:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.21:*:*:*:*:*:*:*
- 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.7.1:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7.2:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7.3:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7.4:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7.5:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7.6:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7.7:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7.8:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7.9:*:*:*:*:*:*:*
- 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:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7:rc3:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.8.0:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.8.1:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.8.2:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.8.3:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.8:beta1:*:*:*:*:*:*
cpe:2.3:o:canonical:ubuntu_linux:12.04:*:*:*:lts:*:*:*+ 2 more
- cpe:2.3:o:canonical:ubuntu_linux:12.04:*:*:*:lts:*:*:*
- cpe:2.3:o:canonical:ubuntu_linux:14.04:*:*:*:lts:*:*:*
- cpe:2.3:o:canonical:ubuntu_linux:15.04:*:*:*:*:*:*:*
- cpe:2.3:o:oracle:solaris:11.3:*:*:*:*:*:*:*
Patches
4575f59f9bc7c[1.4.x] Fixed DoS possiblity in contrib.auth.views.logout()
6 files changed · +133 −25
django/contrib/sessions/backends/base.py+8 −1 modified@@ -128,6 +128,13 @@ def clear(self): self.accessed = True self.modified = True + def is_empty(self): + "Returns True when there is no session_key and the session is empty" + try: + return not bool(self._session_key) and not self._session_cache + except AttributeError: + return True + def _get_new_session_key(self): "Returns session key that isn't being used." # Todo: move to 0-9a-z charset in 1.5 @@ -230,7 +237,7 @@ def flush(self): """ self.clear() self.delete() - self.create() + self._session_key = None def cycle_key(self): """
django/contrib/sessions/backends/cached_db.py+1 −1 modified@@ -58,4 +58,4 @@ def flush(self): """ self.clear() self.delete(self.session_key) - self.create() + self._session_key = None
django/contrib/sessions/middleware.py+27 −19 modified@@ -14,30 +14,38 @@ def process_request(self, request): def process_response(self, request, response): """ If request.session was modified, or if the configuration is to save the - session every time, save the changes and set a session cookie. + session every time, save the changes and set a session cookie or delete + the session cookie if the session has been emptied. """ try: accessed = request.session.accessed modified = request.session.modified + empty = request.session.is_empty() except AttributeError: pass else: - if accessed: - patch_vary_headers(response, ('Cookie',)) - if modified or settings.SESSION_SAVE_EVERY_REQUEST: - if request.session.get_expire_at_browser_close(): - max_age = None - expires = None - else: - max_age = request.session.get_expiry_age() - expires_time = time.time() + max_age - expires = cookie_date(expires_time) - # Save the session data and refresh the client cookie. - request.session.save() - response.set_cookie(settings.SESSION_COOKIE_NAME, - request.session.session_key, max_age=max_age, - expires=expires, domain=settings.SESSION_COOKIE_DOMAIN, - path=settings.SESSION_COOKIE_PATH, - secure=settings.SESSION_COOKIE_SECURE or None, - httponly=settings.SESSION_COOKIE_HTTPONLY or None) + # First check if we need to delete this cookie. + # The session should be deleted only if the session is entirely empty + if settings.SESSION_COOKIE_NAME in request.COOKIES and empty: + response.delete_cookie(settings.SESSION_COOKIE_NAME, + domain=settings.SESSION_COOKIE_DOMAIN) + else: + if accessed: + patch_vary_headers(response, ('Cookie',)) + if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty: + if request.session.get_expire_at_browser_close(): + max_age = None + expires = None + else: + max_age = request.session.get_expiry_age() + expires_time = time.time() + max_age + expires = cookie_date(expires_time) + # Save the session data and refresh the client cookie. + request.session.save() + response.set_cookie(settings.SESSION_COOKIE_NAME, + request.session.session_key, max_age=max_age, + expires=expires, domain=settings.SESSION_COOKIE_DOMAIN, + path=settings.SESSION_COOKIE_PATH, + secure=settings.SESSION_COOKIE_SECURE or None, + httponly=settings.SESSION_COOKIE_HTTPONLY or None) return response
django/contrib/sessions/tests.py+70 −0 modified@@ -150,6 +150,7 @@ def test_flush(self): self.session.flush() self.assertFalse(self.session.exists(prev_key)) self.assertNotEqual(self.session.session_key, prev_key) + self.assertIsNone(self.session.session_key) self.assertTrue(self.session.modified) self.assertTrue(self.session.accessed) @@ -432,6 +433,75 @@ def test_no_httponly_session_cookie(self): self.assertNotIn('httponly', str(response.cookies[settings.SESSION_COOKIE_NAME])) + def test_session_delete_on_end(self): + request = RequestFactory().get('/') + response = HttpResponse('Session test') + middleware = SessionMiddleware() + + # Before deleting, there has to be an existing cookie + request.COOKIES[settings.SESSION_COOKIE_NAME] = 'abc' + + # Simulate a request that ends the session + middleware.process_request(request) + request.session.flush() + + # Handle the response through the middleware + response = middleware.process_response(request, response) + + # Check that the cookie was deleted, not recreated. + # A deleted cookie header looks like: + # Set-Cookie: sessionid=; expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/ + self.assertEqual( + 'Set-Cookie: %s=; expires=Thu, 01-Jan-1970 00:00:00 GMT; ' + 'Max-Age=0; Path=/' % settings.SESSION_COOKIE_NAME, + str(response.cookies[settings.SESSION_COOKIE_NAME]) + ) + + @override_settings(SESSION_COOKIE_DOMAIN='.example.local') + def test_session_delete_on_end_with_custom_domain(self): + request = RequestFactory().get('/') + response = HttpResponse('Session test') + middleware = SessionMiddleware() + + # Before deleting, there has to be an existing cookie + request.COOKIES[settings.SESSION_COOKIE_NAME] = 'abc' + + # Simulate a request that ends the session + middleware.process_request(request) + request.session.flush() + + # Handle the response through the middleware + response = middleware.process_response(request, response) + + # Check that the cookie was deleted, not recreated. + # A deleted cookie header with a custom domain looks like: + # Set-Cookie: sessionid=; Domain=.example.local; + # expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/ + self.assertEqual( + 'Set-Cookie: %s=; Domain=.example.local; expires=Thu, ' + '01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/' % ( + settings.SESSION_COOKIE_NAME, + ), + str(response.cookies[settings.SESSION_COOKIE_NAME]) + ) + + def test_flush_empty_without_session_cookie_doesnt_set_cookie(self): + request = RequestFactory().get('/') + response = HttpResponse('Session test') + middleware = SessionMiddleware() + + # Simulate a request that ends the session + middleware.process_request(request) + request.session.flush() + + # Handle the response through the middleware + response = middleware.process_response(request, response) + + # A cookie should not be set. + self.assertEqual(response.cookies, {}) + # The session is accessed so "Vary: Cookie" should be set. + self.assertEqual(response['Vary'], 'Cookie') + class CookieSessionTests(SessionTestsMixin, TestCase):
docs/releases/1.4.22.txt+18 −0 modified@@ -9,3 +9,21 @@ Django 1.4.22 fixes a security issue in 1.4.21. It also fixes support with pip 7+ by disabling wheel support. Older versions of 1.4 would silently build a broken wheel when installed with those versions of pip. + +Denial-of-service possibility in ``logout()`` view by filling session store +=========================================================================== + +Previously, a session could be created when anonymously accessing the +:func:`django.contrib.auth.views.logout` view (provided it wasn't decorated +with :func:`~django.contrib.auth.decorators.login_required` as done in the +admin). This could allow an attacker to easily create many new session records +by sending repeated requests, potentially filling up the session store or +causing other users' session records to be evicted. + +The :class:`~django.contrib.sessions.middleware.SessionMiddleware` has been +modified to no longer create empty session records. + +Additionally, the ``contrib.sessions.backends.base.SessionBase.flush()`` and +``cache_db.SessionStore.flush()`` methods have been modified to avoid creating +a new empty session. Maintainers of third-party session backends should check +if the same vulnerability is present in their backend and correct it if so.
docs/topics/http/sessions.txt+9 −4 modified@@ -197,12 +197,17 @@ You can edit it multiple times. .. method:: flush - Delete the current session data from the session and regenerate the - session key value that is sent back to the user in the cookie. This is - used if you want to ensure that the previous session data can't be - accessed again from the user's browser (for example, the + Deletes the current session data from the session and deletes the session + cookie. This is used if you want to ensure that the previous session data + can't be accessed again from the user's browser (for example, the :func:`django.contrib.auth.logout()` function calls it). + .. versionchanged:: 1.4.22 + + Deletion of the session cookie was added. Previously, the behavior + was to regenerate the session key value that was sent back to the + user in the cookie, but this was a denial-of-service vulnerability. + .. method:: set_test_cookie Sets a test cookie to determine whether the user's browser supports
2f5485346ee6[1.7.x] Fixed DoS possiblity in contrib.auth.views.logout()
7 files changed · +154 −27
django/contrib/sessions/backends/base.py+8 −1 modified@@ -142,6 +142,13 @@ def clear(self): self.accessed = True self.modified = True + def is_empty(self): + "Returns True when there is no session_key and the session is empty" + try: + return not bool(self._session_key) and not self._session_cache + except AttributeError: + return True + def _get_new_session_key(self): "Returns session key that isn't being used." while True: @@ -268,7 +275,7 @@ def flush(self): """ self.clear() self.delete() - self.create() + self._session_key = None def cycle_key(self): """
django/contrib/sessions/backends/cached_db.py+1 −1 modified@@ -79,7 +79,7 @@ def flush(self): """ self.clear() self.delete(self.session_key) - self.create() + self._session_key = None # At bottom to avoid circular import
django/contrib/sessions/middleware.py+29 −21 modified@@ -18,32 +18,40 @@ def process_request(self, request): def process_response(self, request, response): """ If request.session was modified, or if the configuration is to save the - session every time, save the changes and set a session cookie. + session every time, save the changes and set a session cookie or delete + the session cookie if the session has been emptied. """ try: accessed = request.session.accessed modified = request.session.modified + empty = request.session.is_empty() except AttributeError: pass else: - if accessed: - patch_vary_headers(response, ('Cookie',)) - if modified or settings.SESSION_SAVE_EVERY_REQUEST: - if request.session.get_expire_at_browser_close(): - max_age = None - expires = None - else: - max_age = request.session.get_expiry_age() - expires_time = time.time() + max_age - expires = cookie_date(expires_time) - # Save the session data and refresh the client cookie. - # Skip session save for 500 responses, refs #3881. - if response.status_code != 500: - request.session.save() - response.set_cookie(settings.SESSION_COOKIE_NAME, - request.session.session_key, max_age=max_age, - expires=expires, domain=settings.SESSION_COOKIE_DOMAIN, - path=settings.SESSION_COOKIE_PATH, - secure=settings.SESSION_COOKIE_SECURE or None, - httponly=settings.SESSION_COOKIE_HTTPONLY or None) + # First check if we need to delete this cookie. + # The session should be deleted only if the session is entirely empty + if settings.SESSION_COOKIE_NAME in request.COOKIES and empty: + response.delete_cookie(settings.SESSION_COOKIE_NAME, + domain=settings.SESSION_COOKIE_DOMAIN) + else: + if accessed: + patch_vary_headers(response, ('Cookie',)) + if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty: + if request.session.get_expire_at_browser_close(): + max_age = None + expires = None + else: + max_age = request.session.get_expiry_age() + expires_time = time.time() + max_age + expires = cookie_date(expires_time) + # Save the session data and refresh the client cookie. + # Skip session save for 500 responses, refs #3881. + if response.status_code != 500: + request.session.save() + response.set_cookie(settings.SESSION_COOKIE_NAME, + request.session.session_key, max_age=max_age, + expires=expires, domain=settings.SESSION_COOKIE_DOMAIN, + path=settings.SESSION_COOKIE_PATH, + secure=settings.SESSION_COOKIE_SECURE or None, + httponly=settings.SESSION_COOKIE_HTTPONLY or None) return response
django/contrib/sessions/tests.py+70 −0 modified@@ -159,6 +159,7 @@ def test_flush(self): self.session.flush() self.assertFalse(self.session.exists(prev_key)) self.assertNotEqual(self.session.session_key, prev_key) + self.assertIsNone(self.session.session_key) self.assertTrue(self.session.modified) self.assertTrue(self.session.accessed) @@ -589,6 +590,75 @@ def test_session_save_on_500(self): # Check that the value wasn't saved above. self.assertNotIn('hello', request.session.load()) + def test_session_delete_on_end(self): + request = RequestFactory().get('/') + response = HttpResponse('Session test') + middleware = SessionMiddleware() + + # Before deleting, there has to be an existing cookie + request.COOKIES[settings.SESSION_COOKIE_NAME] = 'abc' + + # Simulate a request that ends the session + middleware.process_request(request) + request.session.flush() + + # Handle the response through the middleware + response = middleware.process_response(request, response) + + # Check that the cookie was deleted, not recreated. + # A deleted cookie header looks like: + # Set-Cookie: sessionid=; expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/ + self.assertEqual( + 'Set-Cookie: {0}=; expires=Thu, 01-Jan-1970 00:00:00 GMT; ' + 'Max-Age=0; Path=/'.format(settings.SESSION_COOKIE_NAME), + str(response.cookies[settings.SESSION_COOKIE_NAME]) + ) + + @override_settings(SESSION_COOKIE_DOMAIN='.example.local') + def test_session_delete_on_end_with_custom_domain(self): + request = RequestFactory().get('/') + response = HttpResponse('Session test') + middleware = SessionMiddleware() + + # Before deleting, there has to be an existing cookie + request.COOKIES[settings.SESSION_COOKIE_NAME] = 'abc' + + # Simulate a request that ends the session + middleware.process_request(request) + request.session.flush() + + # Handle the response through the middleware + response = middleware.process_response(request, response) + + # Check that the cookie was deleted, not recreated. + # A deleted cookie header with a custom domain looks like: + # Set-Cookie: sessionid=; Domain=.example.local; + # expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/ + self.assertEqual( + 'Set-Cookie: {}=; Domain=.example.local; expires=Thu, ' + '01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/'.format( + settings.SESSION_COOKIE_NAME, + ), + str(response.cookies[settings.SESSION_COOKIE_NAME]) + ) + + def test_flush_empty_without_session_cookie_doesnt_set_cookie(self): + request = RequestFactory().get('/') + response = HttpResponse('Session test') + middleware = SessionMiddleware() + + # Simulate a request that ends the session + middleware.process_request(request) + request.session.flush() + + # Handle the response through the middleware + response = middleware.process_response(request, response) + + # A cookie should not be set. + self.assertEqual(response.cookies, {}) + # The session is accessed so "Vary: Cookie" should be set. + self.assertEqual(response['Vary'], 'Cookie') + class CookieSessionTests(SessionTestsMixin, TestCase):
docs/releases/1.4.22.txt+18 −0 modified@@ -9,3 +9,21 @@ Django 1.4.22 fixes a security issue in 1.4.21. It also fixes support with pip 7+ by disabling wheel support. Older versions of 1.4 would silently build a broken wheel when installed with those versions of pip. + +Denial-of-service possibility in ``logout()`` view by filling session store +=========================================================================== + +Previously, a session could be created when anonymously accessing the +:func:`django.contrib.auth.views.logout` view (provided it wasn't decorated +with :func:`~django.contrib.auth.decorators.login_required` as done in the +admin). This could allow an attacker to easily create many new session records +by sending repeated requests, potentially filling up the session store or +causing other users' session records to be evicted. + +The :class:`~django.contrib.sessions.middleware.SessionMiddleware` has been +modified to no longer create empty session records. + +Additionally, the ``contrib.sessions.backends.base.SessionBase.flush()`` and +``cache_db.SessionStore.flush()`` methods have been modified to avoid creating +a new empty session. Maintainers of third-party session backends should check +if the same vulnerability is present in their backend and correct it if so.
docs/releases/1.7.10.txt+18 −0 modified@@ -5,3 +5,21 @@ Django 1.7.10 release notes *August 18, 2015* Django 1.7.10 fixes a security issue in 1.7.9. + +Denial-of-service possibility in ``logout()`` view by filling session store +=========================================================================== + +Previously, a session could be created when anonymously accessing the +:func:`django.contrib.auth.views.logout` view (provided it wasn't decorated +with :func:`~django.contrib.auth.decorators.login_required` as done in the +admin). This could allow an attacker to easily create many new session records +by sending repeated requests, potentially filling up the session store or +causing other users' session records to be evicted. + +The :class:`~django.contrib.sessions.middleware.SessionMiddleware` has been +modified to no longer create empty session records. + +Additionally, the ``contrib.sessions.backends.base.SessionBase.flush()`` and +``cache_db.SessionStore.flush()`` methods have been modified to avoid creating +a new empty session. Maintainers of third-party session backends should check +if the same vulnerability is present in their backend and correct it if so.
docs/topics/http/sessions.txt+10 −4 modified@@ -226,12 +226,18 @@ You can edit it multiple times. .. method:: flush() - Delete the current session data from the session and regenerate the - session key value that is sent back to the user in the cookie. This is - used if you want to ensure that the previous session data can't be - accessed again from the user's browser (for example, the + Deletes the current session data from the session and deletes the session + cookie. This is used if you want to ensure that the previous session data + can't be accessed again from the user's browser (for example, the :func:`django.contrib.auth.logout()` function calls it). + .. versionchanged:: 1.7.10 + + Deletion of the session cookie was added. Previously, the behavior + was to regenerate the session key value that was sent back to the + user in the cookie, but this could be a denial-of-service + vulnerability. + .. method:: set_test_cookie() Sets a test cookie to determine whether the user's browser supports
8cc41ce7a7a8Fixed DoS possiblity in contrib.auth.views.logout()
5 files changed · +67 −1
django/contrib/sessions/middleware.py+1 −1 modified@@ -36,7 +36,7 @@ def process_response(self, request, response): else: if accessed: patch_vary_headers(response, ('Cookie',)) - if modified or settings.SESSION_SAVE_EVERY_REQUEST: + if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty: if request.session.get_expire_at_browser_close(): max_age = None expires = None
docs/releases/1.4.22.txt+18 −0 modified@@ -9,3 +9,21 @@ Django 1.4.22 fixes a security issue in 1.4.21. It also fixes support with pip 7+ by disabling wheel support. Older versions of 1.4 would silently build a broken wheel when installed with those versions of pip. + +Denial-of-service possibility in ``logout()`` view by filling session store +=========================================================================== + +Previously, a session could be created when anonymously accessing the +:func:`django.contrib.auth.views.logout` view (provided it wasn't decorated +with :func:`~django.contrib.auth.decorators.login_required` as done in the +admin). This could allow an attacker to easily create many new session records +by sending repeated requests, potentially filling up the session store or +causing other users' session records to be evicted. + +The :class:`~django.contrib.sessions.middleware.SessionMiddleware` has been +modified to no longer create empty session records. + +Additionally, the ``contrib.sessions.backends.base.SessionBase.flush()`` and +``cache_db.SessionStore.flush()`` methods have been modified to avoid creating +a new empty session. Maintainers of third-party session backends should check +if the same vulnerability is present in their backend and correct it if so.
docs/releases/1.7.10.txt+18 −0 modified@@ -5,3 +5,21 @@ Django 1.7.10 release notes *August 18, 2015* Django 1.7.10 fixes a security issue in 1.7.9. + +Denial-of-service possibility in ``logout()`` view by filling session store +=========================================================================== + +Previously, a session could be created when anonymously accessing the +:func:`django.contrib.auth.views.logout` view (provided it wasn't decorated +with :func:`~django.contrib.auth.decorators.login_required` as done in the +admin). This could allow an attacker to easily create many new session records +by sending repeated requests, potentially filling up the session store or +causing other users' session records to be evicted. + +The :class:`~django.contrib.sessions.middleware.SessionMiddleware` has been +modified to no longer create empty session records. + +Additionally, the ``contrib.sessions.backends.base.SessionBase.flush()`` and +``cache_db.SessionStore.flush()`` methods have been modified to avoid creating +a new empty session. Maintainers of third-party session backends should check +if the same vulnerability is present in their backend and correct it if so.
docs/releases/1.8.4.txt+13 −0 modified@@ -6,6 +6,19 @@ Django 1.8.4 release notes Django 1.8.4 fixes a security issue and several bugs in 1.8.3. +Denial-of-service possibility in ``logout()`` view by filling session store +=========================================================================== + +Previously, a session could be created when anonymously accessing the +:func:`django.contrib.auth.views.logout` view (provided it wasn't decorated +with :func:`~django.contrib.auth.decorators.login_required` as done in the +admin). This could allow an attacker to easily create many new session records +by sending repeated requests, potentially filling up the session store or +causing other users' session records to be evicted. + +The :class:`~django.contrib.sessions.middleware.SessionMiddleware` has been +modified to no longer create empty session records. + Bugfixes ========
tests/sessions_tests/tests.py+17 −0 modified@@ -678,6 +678,23 @@ def test_session_delete_on_end_with_custom_domain(self): str(response.cookies[settings.SESSION_COOKIE_NAME]) ) + def test_flush_empty_without_session_cookie_doesnt_set_cookie(self): + request = RequestFactory().get('/') + response = HttpResponse('Session test') + middleware = SessionMiddleware() + + # Simulate a request that ends the session + middleware.process_request(request) + request.session.flush() + + # Handle the response through the middleware + response = middleware.process_response(request, response) + + # A cookie should not be set. + self.assertEqual(response.cookies, {}) + # The session is accessed so "Vary: Cookie" should be set. + self.assertEqual(response['Vary'], 'Cookie') + # Don't need DB flushing for these tests, so can use unittest.TestCase as base class class CookieSessionTests(SessionTestsMixin, unittest.TestCase):
2eb86b01d7b5[1.8.x] Fixed DoS possiblity in contrib.auth.views.logout()
5 files changed · +67 −1
django/contrib/sessions/middleware.py+1 −1 modified@@ -36,7 +36,7 @@ def process_response(self, request, response): else: if accessed: patch_vary_headers(response, ('Cookie',)) - if modified or settings.SESSION_SAVE_EVERY_REQUEST: + if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty: if request.session.get_expire_at_browser_close(): max_age = None expires = None
docs/releases/1.4.22.txt+18 −0 modified@@ -9,3 +9,21 @@ Django 1.4.22 fixes a security issue in 1.4.21. It also fixes support with pip 7+ by disabling wheel support. Older versions of 1.4 would silently build a broken wheel when installed with those versions of pip. + +Denial-of-service possibility in ``logout()`` view by filling session store +=========================================================================== + +Previously, a session could be created when anonymously accessing the +:func:`django.contrib.auth.views.logout` view (provided it wasn't decorated +with :func:`~django.contrib.auth.decorators.login_required` as done in the +admin). This could allow an attacker to easily create many new session records +by sending repeated requests, potentially filling up the session store or +causing other users' session records to be evicted. + +The :class:`~django.contrib.sessions.middleware.SessionMiddleware` has been +modified to no longer create empty session records. + +Additionally, the ``contrib.sessions.backends.base.SessionBase.flush()`` and +``cache_db.SessionStore.flush()`` methods have been modified to avoid creating +a new empty session. Maintainers of third-party session backends should check +if the same vulnerability is present in their backend and correct it if so.
docs/releases/1.7.10.txt+18 −0 modified@@ -5,3 +5,21 @@ Django 1.7.10 release notes *August 18, 2015* Django 1.7.10 fixes a security issue in 1.7.9. + +Denial-of-service possibility in ``logout()`` view by filling session store +=========================================================================== + +Previously, a session could be created when anonymously accessing the +:func:`django.contrib.auth.views.logout` view (provided it wasn't decorated +with :func:`~django.contrib.auth.decorators.login_required` as done in the +admin). This could allow an attacker to easily create many new session records +by sending repeated requests, potentially filling up the session store or +causing other users' session records to be evicted. + +The :class:`~django.contrib.sessions.middleware.SessionMiddleware` has been +modified to no longer create empty session records. + +Additionally, the ``contrib.sessions.backends.base.SessionBase.flush()`` and +``cache_db.SessionStore.flush()`` methods have been modified to avoid creating +a new empty session. Maintainers of third-party session backends should check +if the same vulnerability is present in their backend and correct it if so.
docs/releases/1.8.4.txt+13 −0 modified@@ -6,6 +6,19 @@ Django 1.8.4 release notes Django 1.8.4 fixes a security issue and several bugs in 1.8.3. +Denial-of-service possibility in ``logout()`` view by filling session store +=========================================================================== + +Previously, a session could be created when anonymously accessing the +:func:`django.contrib.auth.views.logout` view (provided it wasn't decorated +with :func:`~django.contrib.auth.decorators.login_required` as done in the +admin). This could allow an attacker to easily create many new session records +by sending repeated requests, potentially filling up the session store or +causing other users' session records to be evicted. + +The :class:`~django.contrib.sessions.middleware.SessionMiddleware` has been +modified to no longer create empty session records. + Bugfixes ========
tests/sessions_tests/tests.py+17 −0 modified@@ -660,6 +660,23 @@ def test_session_delete_on_end_with_custom_domain(self): str(response.cookies[settings.SESSION_COOKIE_NAME]) ) + def test_flush_empty_without_session_cookie_doesnt_set_cookie(self): + request = RequestFactory().get('/') + response = HttpResponse('Session test') + middleware = SessionMiddleware() + + # Simulate a request that ends the session + middleware.process_request(request) + request.session.flush() + + # Handle the response through the middleware + response = middleware.process_response(request, response) + + # A cookie should not be set. + self.assertEqual(response.cookies, {}) + # The session is accessed so "Vary: Cookie" should be set. + self.assertEqual(response['Vary'], 'Cookie') + # Don't need DB flushing for these tests, so can use unittest.TestCase as base class class CookieSessionTests(SessionTestsMixin, unittest.TestCase):
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
24- www.djangoproject.com/weblog/2015/aug/18/security-releases/nvdPatchVendor Advisory
- www.oracle.com/technetwork/topics/security/bulletinoct2015-2511968.htmlnvdThird Party AdvisoryWEB
- www.ubuntu.com/usn/USN-2720-1nvdThird Party AdvisoryWEB
- github.com/advisories/GHSA-pgxh-wfw4-jx2vghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2015-5963ghsaADVISORY
- lists.fedoraproject.org/pipermail/package-announce/2015-November/172084.htmlnvdWEB
- lists.opensuse.org/opensuse-updates/2015-09/msg00026.htmlnvdWEB
- lists.opensuse.org/opensuse-updates/2015-09/msg00035.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2015-1766.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2015-1767.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2015-1894.htmlnvdWEB
- www.debian.org/security/2015/dsa-3338nvdWEB
- access.redhat.com/errata/RHSA-2015:1876nvdWEB
- github.com/django/django/blob/4555a823fd57e261e1b19c778429473256c8ea08/docs/releases/1.8.4.txtghsaWEB
- github.com/django/django/commit/2eb86b01d7b59be06076f6179a454d0fd0afaff6ghsaWEB
- github.com/django/django/commit/2f5485346ee6f84b4e52068c04e043092daf55f7ghsaWEB
- github.com/django/django/commit/575f59f9bc7c59a5e41a081d1f5f55fc859c5012ghsaWEB
- github.com/django/django/commit/8cc41ce7a7a8f6bebfdd89d5ab276cd0109f4fc5ghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/django/PYSEC-2015-22.yamlghsaWEB
- web.archive.org/web/20150904151934/http://www.securitytracker.com/id/1033318ghsaWEB
- web.archive.org/web/20200228050526/http://www.securityfocus.com/bid/76428ghsaWEB
- www.djangoproject.com/weblog/2015/aug/18/security-releasesghsaWEB
- www.securityfocus.com/bid/76428nvd
- www.securitytracker.com/id/1033318nvd
News mentions
0No linked articles in our index yet.