Fixed #31790 -- Fixed setting SameSite and Secure cookies flags in HttpResponse.delete_cookie().
Cookies with the "SameSite" flag set to None and without the "secure" flag will be soon rejected by latest browser versions. This affects sessions and messages cookies.
This commit is contained in:
parent
156a2138db
commit
240cbb63bf
|
@ -92,7 +92,11 @@ class CookieStorage(BaseStorage):
|
||||||
samesite=settings.SESSION_COOKIE_SAMESITE,
|
samesite=settings.SESSION_COOKIE_SAMESITE,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
response.delete_cookie(self.cookie_name, domain=settings.SESSION_COOKIE_DOMAIN)
|
response.delete_cookie(
|
||||||
|
self.cookie_name,
|
||||||
|
domain=settings.SESSION_COOKIE_DOMAIN,
|
||||||
|
samesite=settings.SESSION_COOKIE_SAMESITE,
|
||||||
|
)
|
||||||
|
|
||||||
def _store(self, messages, response, remove_oldest=True, *args, **kwargs):
|
def _store(self, messages, response, remove_oldest=True, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -42,6 +42,7 @@ class SessionMiddleware(MiddlewareMixin):
|
||||||
settings.SESSION_COOKIE_NAME,
|
settings.SESSION_COOKIE_NAME,
|
||||||
path=settings.SESSION_COOKIE_PATH,
|
path=settings.SESSION_COOKIE_PATH,
|
||||||
domain=settings.SESSION_COOKIE_DOMAIN,
|
domain=settings.SESSION_COOKIE_DOMAIN,
|
||||||
|
samesite=settings.SESSION_COOKIE_SAMESITE,
|
||||||
)
|
)
|
||||||
patch_vary_headers(response, ('Cookie',))
|
patch_vary_headers(response, ('Cookie',))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -210,13 +210,18 @@ class HttpResponseBase:
|
||||||
value = signing.get_cookie_signer(salt=key + salt).sign(value)
|
value = signing.get_cookie_signer(salt=key + salt).sign(value)
|
||||||
return self.set_cookie(key, value, **kwargs)
|
return self.set_cookie(key, value, **kwargs)
|
||||||
|
|
||||||
def delete_cookie(self, key, path='/', domain=None):
|
def delete_cookie(self, key, path='/', domain=None, samesite=None):
|
||||||
# Most browsers ignore the Set-Cookie header if the cookie name starts
|
# Browsers can ignore the Set-Cookie header if the cookie doesn't use
|
||||||
# with __Host- or __Secure- and the cookie doesn't use the secure flag.
|
# the secure flag and:
|
||||||
secure = key.startswith(('__Secure-', '__Host-'))
|
# - the cookie name starts with "__Host-" or "__Secure-", or
|
||||||
|
# - the samesite is "none".
|
||||||
|
secure = (
|
||||||
|
key.startswith(('__Secure-', '__Host-')) or
|
||||||
|
(samesite and samesite.lower() == 'none')
|
||||||
|
)
|
||||||
self.set_cookie(
|
self.set_cookie(
|
||||||
key, max_age=0, path=path, domain=domain, secure=secure,
|
key, max_age=0, path=path, domain=domain, secure=secure,
|
||||||
expires='Thu, 01 Jan 1970 00:00:00 GMT',
|
expires='Thu, 01 Jan 1970 00:00:00 GMT', samesite=samesite,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Common methods used by subclasses
|
# Common methods used by subclasses
|
||||||
|
|
|
@ -890,7 +890,7 @@ Methods
|
||||||
|
|
||||||
Using ``samesite='None'`` (string) was allowed.
|
Using ``samesite='None'`` (string) was allowed.
|
||||||
|
|
||||||
.. method:: HttpResponse.delete_cookie(key, path='/', domain=None)
|
.. method:: HttpResponse.delete_cookie(key, path='/', domain=None, samesite=None)
|
||||||
|
|
||||||
Deletes the cookie with the given key. Fails silently if the key doesn't
|
Deletes the cookie with the given key. Fails silently if the key doesn't
|
||||||
exist.
|
exist.
|
||||||
|
@ -899,6 +899,10 @@ Methods
|
||||||
values you used in ``set_cookie()`` -- otherwise the cookie may not be
|
values you used in ``set_cookie()`` -- otherwise the cookie may not be
|
||||||
deleted.
|
deleted.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.2.15
|
||||||
|
|
||||||
|
The ``samesite`` argument was added.
|
||||||
|
|
||||||
.. method:: HttpResponse.close()
|
.. method:: HttpResponse.close()
|
||||||
|
|
||||||
This method is called at the end of the request directly by the WSGI
|
This method is called at the end of the request directly by the WSGI
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
===========================
|
||||||
|
Django 2.2.15 release notes
|
||||||
|
===========================
|
||||||
|
|
||||||
|
*Expected August 3, 2020*
|
||||||
|
|
||||||
|
Django 2.2.15 fixes a bug in 2.2.14.
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
========
|
||||||
|
|
||||||
|
* Allowed setting the ``SameSite`` cookie flag in
|
||||||
|
:meth:`.HttpResponse.delete_cookie` (:ticket:`31790`).
|
||||||
|
|
||||||
|
* Fixed setting the ``Secure`` cookie flag in
|
||||||
|
:meth:`.HttpResponse.delete_cookie` for cookies that use ``samesite='none'``
|
||||||
|
(:ticket:`31790`).
|
|
@ -9,4 +9,9 @@ Django 3.0.9 fixes several bugs in 3.0.8.
|
||||||
Bugfixes
|
Bugfixes
|
||||||
========
|
========
|
||||||
|
|
||||||
* ...
|
* Allowed setting the ``SameSite`` cookie flag in
|
||||||
|
:meth:`.HttpResponse.delete_cookie` (:ticket:`31790`).
|
||||||
|
|
||||||
|
* Fixed setting the ``Secure`` cookie flag in
|
||||||
|
:meth:`.HttpResponse.delete_cookie` for cookies that use ``samesite='none'``
|
||||||
|
(:ticket:`31790`).
|
||||||
|
|
|
@ -55,6 +55,7 @@ versions of the documentation contain the release notes for any later releases.
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
|
2.2.15
|
||||||
2.2.14
|
2.2.14
|
||||||
2.2.13
|
2.2.13
|
||||||
2.2.12
|
2.2.12
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.messages import constants
|
from django.contrib.messages import constants
|
||||||
from django.contrib.messages.storage.base import Message
|
from django.contrib.messages.storage.base import Message
|
||||||
from django.contrib.messages.storage.cookie import (
|
from django.contrib.messages.storage.cookie import (
|
||||||
|
@ -85,6 +86,10 @@ class CookieTests(BaseTests, SimpleTestCase):
|
||||||
self.assertEqual(response.cookies['messages'].value, '')
|
self.assertEqual(response.cookies['messages'].value, '')
|
||||||
self.assertEqual(response.cookies['messages']['domain'], '.example.com')
|
self.assertEqual(response.cookies['messages']['domain'], '.example.com')
|
||||||
self.assertEqual(response.cookies['messages']['expires'], 'Thu, 01 Jan 1970 00:00:00 GMT')
|
self.assertEqual(response.cookies['messages']['expires'], 'Thu, 01 Jan 1970 00:00:00 GMT')
|
||||||
|
self.assertEqual(
|
||||||
|
response.cookies['messages']['samesite'],
|
||||||
|
settings.SESSION_COOKIE_SAMESITE,
|
||||||
|
)
|
||||||
|
|
||||||
def test_get_bad_cookie(self):
|
def test_get_bad_cookie(self):
|
||||||
request = self.get_request()
|
request = self.get_request()
|
||||||
|
|
|
@ -105,6 +105,7 @@ class DeleteCookieTests(SimpleTestCase):
|
||||||
self.assertEqual(cookie['path'], '/')
|
self.assertEqual(cookie['path'], '/')
|
||||||
self.assertEqual(cookie['secure'], '')
|
self.assertEqual(cookie['secure'], '')
|
||||||
self.assertEqual(cookie['domain'], '')
|
self.assertEqual(cookie['domain'], '')
|
||||||
|
self.assertEqual(cookie['samesite'], '')
|
||||||
|
|
||||||
def test_delete_cookie_secure_prefix(self):
|
def test_delete_cookie_secure_prefix(self):
|
||||||
"""
|
"""
|
||||||
|
@ -118,3 +119,14 @@ class DeleteCookieTests(SimpleTestCase):
|
||||||
cookie_name = '__%s-c' % prefix
|
cookie_name = '__%s-c' % prefix
|
||||||
response.delete_cookie(cookie_name)
|
response.delete_cookie(cookie_name)
|
||||||
self.assertIs(response.cookies[cookie_name]['secure'], True)
|
self.assertIs(response.cookies[cookie_name]['secure'], True)
|
||||||
|
|
||||||
|
def test_delete_cookie_secure_samesite_none(self):
|
||||||
|
# delete_cookie() sets the secure flag if samesite='none'.
|
||||||
|
response = HttpResponse()
|
||||||
|
response.delete_cookie('c', samesite='none')
|
||||||
|
self.assertIs(response.cookies['c']['secure'], True)
|
||||||
|
|
||||||
|
def test_delete_cookie_samesite(self):
|
||||||
|
response = HttpResponse()
|
||||||
|
response.delete_cookie('c', samesite='lax')
|
||||||
|
self.assertEqual(response.cookies['c']['samesite'], 'lax')
|
||||||
|
|
|
@ -758,8 +758,9 @@ class SessionMiddlewareTests(TestCase):
|
||||||
# Set-Cookie: sessionid=; expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Path=/
|
# Set-Cookie: sessionid=; expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Path=/
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'Set-Cookie: {}=""; expires=Thu, 01 Jan 1970 00:00:00 GMT; '
|
'Set-Cookie: {}=""; expires=Thu, 01 Jan 1970 00:00:00 GMT; '
|
||||||
'Max-Age=0; Path=/'.format(
|
'Max-Age=0; Path=/; SameSite={}'.format(
|
||||||
settings.SESSION_COOKIE_NAME,
|
settings.SESSION_COOKIE_NAME,
|
||||||
|
settings.SESSION_COOKIE_SAMESITE,
|
||||||
),
|
),
|
||||||
str(response.cookies[settings.SESSION_COOKIE_NAME])
|
str(response.cookies[settings.SESSION_COOKIE_NAME])
|
||||||
)
|
)
|
||||||
|
@ -789,8 +790,9 @@ class SessionMiddlewareTests(TestCase):
|
||||||
# Path=/example/
|
# Path=/example/
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'Set-Cookie: {}=""; Domain=.example.local; expires=Thu, '
|
'Set-Cookie: {}=""; Domain=.example.local; expires=Thu, '
|
||||||
'01 Jan 1970 00:00:00 GMT; Max-Age=0; Path=/example/'.format(
|
'01 Jan 1970 00:00:00 GMT; Max-Age=0; Path=/example/; SameSite={}'.format(
|
||||||
settings.SESSION_COOKIE_NAME,
|
settings.SESSION_COOKIE_NAME,
|
||||||
|
settings.SESSION_COOKIE_SAMESITE,
|
||||||
),
|
),
|
||||||
str(response.cookies[settings.SESSION_COOKIE_NAME])
|
str(response.cookies[settings.SESSION_COOKIE_NAME])
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue