VYPR
Moderate severityNVD Advisory· Published Aug 24, 2015· Updated May 6, 2026

CVE-2015-5964

CVE-2015-5964

Description

The (1) contrib.sessions.backends.base.SessionBase.flush and (2) cache_db.SessionStore.flush functions in Django 1.7.x before 1.7.10, 1.4.x before 1.4.22, and possibly other versions create empty sessions in certain circumstances, which allows remote attackers to cause a denial of service (session store consumption) via unspecified vectors.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
DjangoPyPI
>= 1.7, < 1.7.101.7.10
DjangoPyPI
>= 1.4, < 1.4.221.4.22

Affected products

43
  • cpe: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

3
2f5485346ee6

[1.7.x] Fixed DoS possiblity in contrib.auth.views.logout()

https://github.com/django/djangoTim GrahamAug 5, 2015via ghsa
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
    
575f59f9bc7c

[1.4.x] Fixed DoS possiblity in contrib.auth.views.logout()

https://github.com/django/djangoTim GrahamAug 5, 2015via ghsa
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
    
8cc41ce7a7a8

Fixed DoS possiblity in contrib.auth.views.logout()

https://github.com/django/djangoTim GrahamAug 5, 2015via ghsa
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):
    

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

20

News mentions

0

No linked articles in our index yet.