From b33bfc383935cd26e19a2cf71d066ac6edd1425f Mon Sep 17 00:00:00 2001 From: Osaetin Daniel Date: Wed, 9 Oct 2019 07:42:55 -0400 Subject: [PATCH] Fixed #30862 -- Allowed setting SameSite cookies flags to 'none'. Thanks Florian Apolloner and Carlton Gibson for reviews. --- django/http/response.py | 4 ++-- docs/ref/request-response.txt | 11 +++++++++++ docs/ref/settings.txt | 17 ++++++++++++++++- docs/releases/3.1.txt | 16 +++++++++++++--- tests/responses/test_cookie.py | 5 ++++- 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/django/http/response.py b/django/http/response.py index 269953c0aff..596732a5b87 100644 --- a/django/http/response.py +++ b/django/http/response.py @@ -197,8 +197,8 @@ class HttpResponseBase: if httponly: self.cookies[key]['httponly'] = True if samesite: - if samesite.lower() not in ('lax', 'strict'): - raise ValueError('samesite must be "lax" or "strict".') + if samesite.lower() not in ('lax', 'none', 'strict'): + raise ValueError('samesite must be "lax", "none", or "strict".') self.cookies[key]['samesite'] = samesite def setdefault(self, key, value): diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index e095787363a..7a8b4b3082f 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -833,9 +833,16 @@ Methods isn't supported by all browsers, so it's not a replacement for Django's 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 .. _SameSite: https://www.owasp.org/index.php/SameSite + .. versionchanged:: 3.1 + + Using ``samesite='None'`` (string) was allowed. + .. warning:: :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 :meth:`HttpRequest.get_signed_cookie` call. + .. versionchanged:: 3.1 + + Using ``samesite='None'`` (string) was allowed. + .. method:: HttpResponse.delete_cookie(key, path='/', domain=None) Deletes the cookie with the given key. Fails silently if the key doesn't diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 3c360cf2841..1d92c500260 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -383,6 +383,10 @@ cookie from being sent in cross-site requests. See :setting:`SESSION_COOKIE_SAMESITE` for details about ``SameSite``. +.. versionchanged:: 3.1 + + Setting ``CSRF_COOKIE_SAMESITE = 'None'`` was allowed. + .. setting:: 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``. +.. versionchanged:: 3.1 + + Setting ``LANGUAGE_COOKIE_SAMESITE = 'None'`` was allowed. + .. setting:: 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 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 diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt index 7ae21330a6d..2e15f628609 100644 --- a/docs/releases/3.1.txt +++ b/docs/releases/3.1.txt @@ -105,7 +105,9 @@ Minor features :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` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -141,7 +143,9 @@ Cache 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 ~~~~~ @@ -173,7 +177,9 @@ Generic Views 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 ~~~~~~~ @@ -232,6 +238,10 @@ Requests and Responses * If :setting:`ALLOWED_HOSTS` is empty and ``DEBUG=True``, subdomains of 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 ~~~~~~~~~~~~~ diff --git a/tests/responses/test_cookie.py b/tests/responses/test_cookie.py index ed881435e44..36cb43ed81f 100644 --- a/tests/responses/test_cookie.py +++ b/tests/responses/test_cookie.py @@ -81,13 +81,16 @@ class SetCookieTests(SimpleTestCase): def test_samesite(self): response = HttpResponse() + response.set_cookie('example', samesite='None') + self.assertEqual(response.cookies['example']['samesite'], 'None') response.set_cookie('example', samesite='Lax') self.assertEqual(response.cookies['example']['samesite'], 'Lax') response.set_cookie('example', samesite='strict') self.assertEqual(response.cookies['example']['samesite'], 'strict') 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')