Fixed #30862 -- Allowed setting SameSite cookies flags to 'none'.

Thanks Florian Apolloner and Carlton Gibson for reviews.
This commit is contained in:
Osaetin Daniel 2019-10-09 07:42:55 -04:00 committed by Mariusz Felisiak
parent 14e690ae5a
commit b33bfc3839
5 changed files with 46 additions and 7 deletions

View File

@ -197,8 +197,8 @@ class HttpResponseBase:
if httponly: if httponly:
self.cookies[key]['httponly'] = True self.cookies[key]['httponly'] = True
if samesite: if samesite:
if samesite.lower() not in ('lax', 'strict'): if samesite.lower() not in ('lax', 'none', 'strict'):
raise ValueError('samesite must be "lax" or "strict".') raise ValueError('samesite must be "lax", "none", or "strict".')
self.cookies[key]['samesite'] = samesite self.cookies[key]['samesite'] = samesite
def setdefault(self, key, value): def setdefault(self, key, value):

View File

@ -833,9 +833,16 @@ Methods
isn't supported by all browsers, so it's not a replacement for Django's isn't supported by all browsers, so it's not a replacement for Django's
CSRF protection, but rather a defense in depth measure. CSRF protection, but rather a defense in depth measure.
Use ``samesite='None'`` (string) to explicitly state that this cookie is
sent with all same-site and cross-site requests.
.. _HttpOnly: https://www.owasp.org/index.php/HttpOnly .. _HttpOnly: https://www.owasp.org/index.php/HttpOnly
.. _SameSite: https://www.owasp.org/index.php/SameSite .. _SameSite: https://www.owasp.org/index.php/SameSite
.. versionchanged:: 3.1
Using ``samesite='None'`` (string) was allowed.
.. warning:: .. warning::
:rfc:`RFC 6265 <6265#section-6.1>` states that user agents should :rfc:`RFC 6265 <6265#section-6.1>` states that user agents should
@ -853,6 +860,10 @@ Methods
you will need to remember to pass it to the corresponding you will need to remember to pass it to the corresponding
:meth:`HttpRequest.get_signed_cookie` call. :meth:`HttpRequest.get_signed_cookie` call.
.. versionchanged:: 3.1
Using ``samesite='None'`` (string) was allowed.
.. method:: HttpResponse.delete_cookie(key, path='/', domain=None) .. method:: HttpResponse.delete_cookie(key, path='/', domain=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

View File

@ -383,6 +383,10 @@ cookie from being sent in cross-site requests.
See :setting:`SESSION_COOKIE_SAMESITE` for details about ``SameSite``. See :setting:`SESSION_COOKIE_SAMESITE` for details about ``SameSite``.
.. versionchanged:: 3.1
Setting ``CSRF_COOKIE_SAMESITE = 'None'`` was allowed.
.. setting:: CSRF_COOKIE_SECURE .. setting:: CSRF_COOKIE_SECURE
``CSRF_COOKIE_SECURE`` ``CSRF_COOKIE_SECURE``
@ -1862,6 +1866,10 @@ cookie from being sent in cross-site requests.
See :setting:`SESSION_COOKIE_SAMESITE` for details about ``SameSite``. See :setting:`SESSION_COOKIE_SAMESITE` for details about ``SameSite``.
.. versionchanged:: 3.1
Setting ``LANGUAGE_COOKIE_SAMESITE = 'None'`` was allowed.
.. setting:: LANGUAGE_COOKIE_SECURE .. setting:: LANGUAGE_COOKIE_SECURE
``LANGUAGE_COOKIE_SECURE`` ``LANGUAGE_COOKIE_SECURE``
@ -3208,7 +3216,14 @@ Possible values for the setting are:
regular link from an external website and be blocked in CSRF-prone request regular link from an external website and be blocked in CSRF-prone request
methods (e.g. ``POST``). methods (e.g. ``POST``).
* ``None``: disables the flag. * ``'None'`` (string): the session cookie will be sent with all same-site and
cross-site requests.
* ``False``: disables the flag.
.. versionchanged:: 3.1
Setting ``SESSION_COOKIE_SAMESITE = 'None'`` was allowed.
.. _SameSite: https://www.owasp.org/index.php/SameSite .. _SameSite: https://www.owasp.org/index.php/SameSite

View File

@ -105,7 +105,9 @@ Minor features
:mod:`django.contrib.sessions` :mod:`django.contrib.sessions`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ... * The :setting:`SESSION_COOKIE_SAMESITE` setting now allows ``'None'`` (string)
value to explicitly state that the cookie is sent with all same-site and
cross-site requests.
:mod:`django.contrib.sitemaps` :mod:`django.contrib.sitemaps`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -141,7 +143,9 @@ Cache
CSRF CSRF
~~~~ ~~~~
* ... * The :setting:`CSRF_COOKIE_SAMESITE` setting now allows ``'None'`` (string)
value to explicitly state that the cookie is sent with all same-site and
cross-site requests.
Email Email
~~~~~ ~~~~~
@ -173,7 +177,9 @@ Generic Views
Internationalization Internationalization
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
* ... * The :setting:`LANGUAGE_COOKIE_SAMESITE` setting now allows ``'None'``
(string) value to explicitly state that the cookie is sent with all same-site
and cross-site requests.
Logging Logging
~~~~~~~ ~~~~~~~
@ -232,6 +238,10 @@ Requests and Responses
* If :setting:`ALLOWED_HOSTS` is empty and ``DEBUG=True``, subdomains of * If :setting:`ALLOWED_HOSTS` is empty and ``DEBUG=True``, subdomains of
localhost are now allowed in the ``Host`` header, e.g. ``static.localhost``. localhost are now allowed in the ``Host`` header, e.g. ``static.localhost``.
* :meth:`.HttpResponse.set_cookie` and :meth:`.HttpResponse.set_signed_cookie`
now allow using ``samesite='None'`` (string) to explicitly state that the
cookie is sent with all same-site and cross-site requests.
Serialization Serialization
~~~~~~~~~~~~~ ~~~~~~~~~~~~~

View File

@ -81,13 +81,16 @@ class SetCookieTests(SimpleTestCase):
def test_samesite(self): def test_samesite(self):
response = HttpResponse() response = HttpResponse()
response.set_cookie('example', samesite='None')
self.assertEqual(response.cookies['example']['samesite'], 'None')
response.set_cookie('example', samesite='Lax') response.set_cookie('example', samesite='Lax')
self.assertEqual(response.cookies['example']['samesite'], 'Lax') self.assertEqual(response.cookies['example']['samesite'], 'Lax')
response.set_cookie('example', samesite='strict') response.set_cookie('example', samesite='strict')
self.assertEqual(response.cookies['example']['samesite'], 'strict') self.assertEqual(response.cookies['example']['samesite'], 'strict')
def test_invalid_samesite(self): def test_invalid_samesite(self):
with self.assertRaisesMessage(ValueError, 'samesite must be "lax" or "strict".'): msg = 'samesite must be "lax", "none", or "strict".'
with self.assertRaisesMessage(ValueError, msg):
HttpResponse().set_cookie('example', samesite='invalid') HttpResponse().set_cookie('example', samesite='invalid')