Fixed #27863 -- Added support for the SameSite cookie flag.
Thanks Alex Gaynor for contributing to the patch.
This commit is contained in:
parent
13efbb233a
commit
9a56b4b13e
|
@ -461,6 +461,9 @@ SESSION_COOKIE_SECURE = False
|
||||||
SESSION_COOKIE_PATH = '/'
|
SESSION_COOKIE_PATH = '/'
|
||||||
# Whether to use the non-RFC standard httpOnly flag (IE, FF3+, others)
|
# Whether to use the non-RFC standard httpOnly flag (IE, FF3+, others)
|
||||||
SESSION_COOKIE_HTTPONLY = True
|
SESSION_COOKIE_HTTPONLY = True
|
||||||
|
# Whether to set the flag restricting cookie leaks on cross-site requests.
|
||||||
|
# This can be 'Lax', 'Strict', or None to disable the flag.
|
||||||
|
SESSION_COOKIE_SAMESITE = 'Lax'
|
||||||
# Whether to save the session data on every request.
|
# Whether to save the session data on every request.
|
||||||
SESSION_SAVE_EVERY_REQUEST = False
|
SESSION_SAVE_EVERY_REQUEST = False
|
||||||
# Whether a user's session cookie expires when the Web browser is closed.
|
# Whether a user's session cookie expires when the Web browser is closed.
|
||||||
|
@ -537,6 +540,7 @@ CSRF_COOKIE_DOMAIN = None
|
||||||
CSRF_COOKIE_PATH = '/'
|
CSRF_COOKIE_PATH = '/'
|
||||||
CSRF_COOKIE_SECURE = False
|
CSRF_COOKIE_SECURE = False
|
||||||
CSRF_COOKIE_HTTPONLY = False
|
CSRF_COOKIE_HTTPONLY = False
|
||||||
|
CSRF_COOKIE_SAMESITE = 'Lax'
|
||||||
CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN'
|
CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN'
|
||||||
CSRF_TRUSTED_ORIGINS = []
|
CSRF_TRUSTED_ORIGINS = []
|
||||||
CSRF_USE_SESSIONS = False
|
CSRF_USE_SESSIONS = False
|
||||||
|
|
|
@ -86,6 +86,7 @@ class CookieStorage(BaseStorage):
|
||||||
domain=settings.SESSION_COOKIE_DOMAIN,
|
domain=settings.SESSION_COOKIE_DOMAIN,
|
||||||
secure=settings.SESSION_COOKIE_SECURE or None,
|
secure=settings.SESSION_COOKIE_SECURE or None,
|
||||||
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
|
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
|
||||||
|
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)
|
||||||
|
|
|
@ -69,5 +69,6 @@ class SessionMiddleware(MiddlewareMixin):
|
||||||
path=settings.SESSION_COOKIE_PATH,
|
path=settings.SESSION_COOKIE_PATH,
|
||||||
secure=settings.SESSION_COOKIE_SECURE or None,
|
secure=settings.SESSION_COOKIE_SECURE or None,
|
||||||
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
|
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
|
||||||
|
samesite=settings.SESSION_COOKIE_SAMESITE,
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -3,6 +3,9 @@ from http import cookies
|
||||||
# For backwards compatibility in Django 2.1.
|
# For backwards compatibility in Django 2.1.
|
||||||
SimpleCookie = cookies.SimpleCookie
|
SimpleCookie = cookies.SimpleCookie
|
||||||
|
|
||||||
|
# Add support for the SameSite attribute (obsolete when PY37 is unsupported).
|
||||||
|
cookies.Morsel._reserved.setdefault('samesite', 'SameSite')
|
||||||
|
|
||||||
|
|
||||||
def parse_cookie(cookie):
|
def parse_cookie(cookie):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -154,7 +154,7 @@ class HttpResponseBase:
|
||||||
return self._headers.get(header.lower(), (None, alternate))[1]
|
return self._headers.get(header.lower(), (None, alternate))[1]
|
||||||
|
|
||||||
def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
|
def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
|
||||||
domain=None, secure=False, httponly=False):
|
domain=None, secure=False, httponly=False, samesite=None):
|
||||||
"""
|
"""
|
||||||
Set a cookie.
|
Set a cookie.
|
||||||
|
|
||||||
|
@ -194,6 +194,10 @@ class HttpResponseBase:
|
||||||
self.cookies[key]['secure'] = True
|
self.cookies[key]['secure'] = True
|
||||||
if httponly:
|
if httponly:
|
||||||
self.cookies[key]['httponly'] = True
|
self.cookies[key]['httponly'] = True
|
||||||
|
if samesite:
|
||||||
|
if samesite.lower() not in ('lax', 'strict'):
|
||||||
|
raise ValueError('samesite must be "lax" or "strict".')
|
||||||
|
self.cookies[key]['samesite'] = samesite
|
||||||
|
|
||||||
def setdefault(self, key, value):
|
def setdefault(self, key, value):
|
||||||
"""Set a header unless it has already been set."""
|
"""Set a header unless it has already been set."""
|
||||||
|
|
|
@ -190,6 +190,7 @@ class CsrfViewMiddleware(MiddlewareMixin):
|
||||||
path=settings.CSRF_COOKIE_PATH,
|
path=settings.CSRF_COOKIE_PATH,
|
||||||
secure=settings.CSRF_COOKIE_SECURE,
|
secure=settings.CSRF_COOKIE_SECURE,
|
||||||
httponly=settings.CSRF_COOKIE_HTTPONLY,
|
httponly=settings.CSRF_COOKIE_HTTPONLY,
|
||||||
|
samesite=settings.CSRF_COOKIE_SAMESITE,
|
||||||
)
|
)
|
||||||
# Set the Vary header since content varies with the CSRF cookie.
|
# Set the Vary header since content varies with the CSRF cookie.
|
||||||
patch_vary_headers(response, ('Cookie',))
|
patch_vary_headers(response, ('Cookie',))
|
||||||
|
|
|
@ -513,6 +513,7 @@ A number of settings can be used to control Django's CSRF behavior:
|
||||||
* :setting:`CSRF_COOKIE_HTTPONLY`
|
* :setting:`CSRF_COOKIE_HTTPONLY`
|
||||||
* :setting:`CSRF_COOKIE_NAME`
|
* :setting:`CSRF_COOKIE_NAME`
|
||||||
* :setting:`CSRF_COOKIE_PATH`
|
* :setting:`CSRF_COOKIE_PATH`
|
||||||
|
* :setting:`CSRF_COOKIE_SAMESITE`
|
||||||
* :setting:`CSRF_COOKIE_SECURE`
|
* :setting:`CSRF_COOKIE_SECURE`
|
||||||
* :setting:`CSRF_FAILURE_VIEW`
|
* :setting:`CSRF_FAILURE_VIEW`
|
||||||
* :setting:`CSRF_HEADER_NAME`
|
* :setting:`CSRF_HEADER_NAME`
|
||||||
|
|
|
@ -748,7 +748,7 @@ Methods
|
||||||
|
|
||||||
Sets a header unless it has already been set.
|
Sets a header unless it has already been set.
|
||||||
|
|
||||||
.. method:: HttpResponse.set_cookie(key, value='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=False)
|
.. method:: HttpResponse.set_cookie(key, value='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=False, samesite=None)
|
||||||
|
|
||||||
Sets a cookie. The parameters are the same as in the
|
Sets a cookie. The parameters are the same as in the
|
||||||
:class:`~http.cookies.Morsel` cookie object in the Python standard library.
|
:class:`~http.cookies.Morsel` cookie object in the Python standard library.
|
||||||
|
@ -773,8 +773,17 @@ Methods
|
||||||
when it is honored, it can be a useful way to mitigate the
|
when it is honored, it can be a useful way to mitigate the
|
||||||
risk of a client-side script from accessing the protected cookie
|
risk of a client-side script from accessing the protected cookie
|
||||||
data.
|
data.
|
||||||
|
* Use ``samesite='Strict'`` or ``samesite='Lax'`` to tell the browser not
|
||||||
|
to send this cookie when performing a cross-origin request. `SameSite`_
|
||||||
|
isn't supported by all browsers, so it's not a replacement for Django's
|
||||||
|
CSRF protection, but rather a defense in depth measure.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.1
|
||||||
|
|
||||||
|
The ``samesite`` argument was added.
|
||||||
|
|
||||||
.. _HTTPOnly: https://www.owasp.org/index.php/HTTPOnly
|
.. _HTTPOnly: https://www.owasp.org/index.php/HTTPOnly
|
||||||
|
.. _SameSite: https://www.owasp.org/index.php/SameSite
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
|
@ -784,7 +793,7 @@ Methods
|
||||||
to store a cookie of more than 4096 bytes, but many browsers will not
|
to store a cookie of more than 4096 bytes, but many browsers will not
|
||||||
set the cookie correctly.
|
set the cookie correctly.
|
||||||
|
|
||||||
.. method:: HttpResponse.set_signed_cookie(key, value, salt='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=True)
|
.. method:: HttpResponse.set_signed_cookie(key, value, salt='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=True, samesite=None)
|
||||||
|
|
||||||
Like :meth:`~HttpResponse.set_cookie()`, but
|
Like :meth:`~HttpResponse.set_cookie()`, but
|
||||||
:doc:`cryptographic signing </topics/signing>` the cookie before setting
|
:doc:`cryptographic signing </topics/signing>` the cookie before setting
|
||||||
|
|
|
@ -365,6 +365,20 @@ This is useful if you have multiple Django instances running under the same
|
||||||
hostname. They can use different cookie paths, and each instance will only see
|
hostname. They can use different cookie paths, and each instance will only see
|
||||||
its own CSRF cookie.
|
its own CSRF cookie.
|
||||||
|
|
||||||
|
.. setting:: CSRF_COOKIE_SAMESITE
|
||||||
|
|
||||||
|
``CSRF_COOKIE_SAMESITE``
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
.. versionadded:: 2.1
|
||||||
|
|
||||||
|
Default: ``'Lax'``
|
||||||
|
|
||||||
|
The value of the `SameSite`_ flag on the CSRF cookie. This flag prevents the
|
||||||
|
cookie from being sent in cross-site requests.
|
||||||
|
|
||||||
|
See :setting:`SESSION_COOKIE_SAMESITE` for details about ``SameSite``.
|
||||||
|
|
||||||
.. setting:: CSRF_COOKIE_SECURE
|
.. setting:: CSRF_COOKIE_SECURE
|
||||||
|
|
||||||
``CSRF_COOKIE_SECURE``
|
``CSRF_COOKIE_SECURE``
|
||||||
|
@ -3025,6 +3039,44 @@ This is useful if you have multiple Django instances running under the same
|
||||||
hostname. They can use different cookie paths, and each instance will only see
|
hostname. They can use different cookie paths, and each instance will only see
|
||||||
its own session cookie.
|
its own session cookie.
|
||||||
|
|
||||||
|
.. setting:: SESSION_COOKIE_SAMESITE
|
||||||
|
|
||||||
|
``SESSION_COOKIE_SAMESITE``
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
.. versionadded:: 2.1
|
||||||
|
|
||||||
|
Default: ``'Lax'``
|
||||||
|
|
||||||
|
The value of the `SameSite`_ flag on the session cookie. This flag prevents the
|
||||||
|
cookie from being sent in cross-site requests thus preventing CSRF attacks and
|
||||||
|
making some methods of stealing session cookie impossible.
|
||||||
|
|
||||||
|
Possible values for the setting are:
|
||||||
|
|
||||||
|
* ``'Strict'``: prevents the cookie from being sent by the browser to the
|
||||||
|
target site in all cross-site browsing context, even when following a regular
|
||||||
|
link.
|
||||||
|
|
||||||
|
For example, for a GitHub-like website this would mean that if a logged-in
|
||||||
|
user follows a link to a private GitHub project posted on a corporate
|
||||||
|
discussion forum or email, GitHub will not receive the session cookie and the
|
||||||
|
user won't be able to access the project. A bank website, however, most
|
||||||
|
likely doesn't want to allow any transactional pages to be linked from
|
||||||
|
external sites so the ``'Strict'`` flag would be appropriate.
|
||||||
|
|
||||||
|
* ``'Lax'`` (default): provides a balance between security and usability for
|
||||||
|
websites that want to maintain user's logged-in session after the user
|
||||||
|
arrives from an external link.
|
||||||
|
|
||||||
|
In the GitHub scenario, the session cookie would be allowed when following a
|
||||||
|
regular link from an external website and be blocked in CSRF-prone request
|
||||||
|
methods (e.g. ``POST``).
|
||||||
|
|
||||||
|
* ``None``: disables the flag.
|
||||||
|
|
||||||
|
.. _SameSite: https://www.owasp.org/index.php/SameSite
|
||||||
|
|
||||||
.. setting:: SESSION_COOKIE_SECURE
|
.. setting:: SESSION_COOKIE_SECURE
|
||||||
|
|
||||||
``SESSION_COOKIE_SECURE``
|
``SESSION_COOKIE_SECURE``
|
||||||
|
@ -3425,6 +3477,7 @@ Security
|
||||||
* :setting:`CSRF_COOKIE_DOMAIN`
|
* :setting:`CSRF_COOKIE_DOMAIN`
|
||||||
* :setting:`CSRF_COOKIE_NAME`
|
* :setting:`CSRF_COOKIE_NAME`
|
||||||
* :setting:`CSRF_COOKIE_PATH`
|
* :setting:`CSRF_COOKIE_PATH`
|
||||||
|
* :setting:`CSRF_COOKIE_SAMESITE`
|
||||||
* :setting:`CSRF_COOKIE_SECURE`
|
* :setting:`CSRF_COOKIE_SECURE`
|
||||||
* :setting:`CSRF_FAILURE_VIEW`
|
* :setting:`CSRF_FAILURE_VIEW`
|
||||||
* :setting:`CSRF_HEADER_NAME`
|
* :setting:`CSRF_HEADER_NAME`
|
||||||
|
|
|
@ -112,7 +112,8 @@ Minor features
|
||||||
:mod:`django.contrib.sessions`
|
:mod:`django.contrib.sessions`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
* ...
|
* Added the :setting:`SESSION_COOKIE_SAMESITE` setting to set the ``SameSite``
|
||||||
|
cookie flag on session cookies.
|
||||||
|
|
||||||
:mod:`django.contrib.sitemaps`
|
:mod:`django.contrib.sitemaps`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -143,7 +144,8 @@ Cache
|
||||||
CSRF
|
CSRF
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
* ...
|
* Added the :setting:`CSRF_COOKIE_SAMESITE` setting to set the ``SameSite``
|
||||||
|
cookie flag on CSRF cookies.
|
||||||
|
|
||||||
Database backends
|
Database backends
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
@ -239,6 +241,9 @@ Requests and Responses
|
||||||
|
|
||||||
* Added :meth:`.HttpRequest.get_full_path_info`.
|
* Added :meth:`.HttpRequest.get_full_path_info`.
|
||||||
|
|
||||||
|
* Added the ``samesite`` argument to :meth:`.HttpResponse.set_cookie` to allow
|
||||||
|
setting the ``SameSite`` cookie flag.
|
||||||
|
|
||||||
Serialization
|
Serialization
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -338,6 +343,16 @@ variable now appears as an attribute of each option. For example, in a custom
|
||||||
``input_option.html`` template, change ``{% if wrap_label %}`` to
|
``input_option.html`` template, change ``{% if wrap_label %}`` to
|
||||||
``{% if widget.wrap_label %}``.
|
``{% if widget.wrap_label %}``.
|
||||||
|
|
||||||
|
``SameSite`` cookies
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The cookies used for ``django.contrib.sessions``, ``django.contrib.messages``,
|
||||||
|
and Django's CSRF protection now set the ``SameSite`` flag to ``Lax`` by
|
||||||
|
default. Browsers that respect this flag won't send these cookies on
|
||||||
|
cross-origin requests. If you rely on the old behavior, set the
|
||||||
|
:setting:`SESSION_COOKIE_SAMESITE` and/or :setting:`CSRF_COOKIE_SAMESITE`
|
||||||
|
setting to ``None``.
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
@ -629,6 +629,7 @@ behavior:
|
||||||
* :setting:`SESSION_COOKIE_HTTPONLY`
|
* :setting:`SESSION_COOKIE_HTTPONLY`
|
||||||
* :setting:`SESSION_COOKIE_NAME`
|
* :setting:`SESSION_COOKIE_NAME`
|
||||||
* :setting:`SESSION_COOKIE_PATH`
|
* :setting:`SESSION_COOKIE_PATH`
|
||||||
|
* :setting:`SESSION_COOKIE_SAMESITE`
|
||||||
* :setting:`SESSION_COOKIE_SECURE`
|
* :setting:`SESSION_COOKIE_SECURE`
|
||||||
* :setting:`SESSION_ENGINE`
|
* :setting:`SESSION_ENGINE`
|
||||||
* :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE`
|
* :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE`
|
||||||
|
|
|
@ -586,6 +586,14 @@ class CsrfViewMiddlewareTests(CsrfViewMiddlewareTestMixin, SimpleTestCase):
|
||||||
max_age = resp2.cookies.get('csrfcookie').get('max-age')
|
max_age = resp2.cookies.get('csrfcookie').get('max-age')
|
||||||
self.assertEqual(max_age, '')
|
self.assertEqual(max_age, '')
|
||||||
|
|
||||||
|
def test_csrf_cookie_samesite(self):
|
||||||
|
req = self._get_GET_no_csrf_cookie_request()
|
||||||
|
with self.settings(CSRF_COOKIE_NAME='csrfcookie', CSRF_COOKIE_SAMESITE='Strict'):
|
||||||
|
self.mw.process_view(req, token_view, (), {})
|
||||||
|
resp = token_view(req)
|
||||||
|
resp2 = self.mw.process_response(req, resp)
|
||||||
|
self.assertEqual(resp2.cookies['csrfcookie']['samesite'], 'Strict')
|
||||||
|
|
||||||
def test_process_view_token_too_long(self):
|
def test_process_view_token_too_long(self):
|
||||||
"""
|
"""
|
||||||
If the token is longer than expected, it is ignored and a new token is
|
If the token is longer than expected, it is ignored and a new token is
|
||||||
|
|
|
@ -746,6 +746,11 @@ class CookieTests(unittest.TestCase):
|
||||||
# document.cookie parses whitespace.
|
# document.cookie parses whitespace.
|
||||||
self.assertEqual(parse_cookie(' = b ; ; = ; c = ; '), {'': 'b', 'c': ''})
|
self.assertEqual(parse_cookie(' = b ; ; = ; c = ; '), {'': 'b', 'c': ''})
|
||||||
|
|
||||||
|
def test_samesite(self):
|
||||||
|
c = SimpleCookie('name=value; samesite=lax; httponly')
|
||||||
|
self.assertEqual(c['name']['samesite'], 'lax')
|
||||||
|
self.assertIn('SameSite=lax', c.output())
|
||||||
|
|
||||||
def test_httponly_after_load(self):
|
def test_httponly_after_load(self):
|
||||||
c = SimpleCookie()
|
c = SimpleCookie()
|
||||||
c.load("name=val")
|
c.load("name=val")
|
||||||
|
|
|
@ -57,6 +57,7 @@ class CookieTests(BaseTests, SimpleTestCase):
|
||||||
# The message contains what's expected.
|
# The message contains what's expected.
|
||||||
self.assertEqual(list(storage), example_messages)
|
self.assertEqual(list(storage), example_messages)
|
||||||
|
|
||||||
|
@override_settings(SESSION_COOKIE_SAMESITE='Strict')
|
||||||
def test_cookie_setings(self):
|
def test_cookie_setings(self):
|
||||||
"""
|
"""
|
||||||
CookieStorage honors SESSION_COOKIE_DOMAIN, SESSION_COOKIE_SECURE, and
|
CookieStorage honors SESSION_COOKIE_DOMAIN, SESSION_COOKIE_SECURE, and
|
||||||
|
@ -72,6 +73,7 @@ class CookieTests(BaseTests, SimpleTestCase):
|
||||||
self.assertEqual(response.cookies['messages']['expires'], '')
|
self.assertEqual(response.cookies['messages']['expires'], '')
|
||||||
self.assertIs(response.cookies['messages']['secure'], True)
|
self.assertIs(response.cookies['messages']['secure'], True)
|
||||||
self.assertIs(response.cookies['messages']['httponly'], True)
|
self.assertIs(response.cookies['messages']['httponly'], True)
|
||||||
|
self.assertEqual(response.cookies['messages']['samesite'], 'Strict')
|
||||||
|
|
||||||
# Test deletion of the cookie (storing with an empty value) after the messages have been consumed
|
# Test deletion of the cookie (storing with an empty value) after the messages have been consumed
|
||||||
storage = self.get_storage()
|
storage = self.get_storage()
|
||||||
|
|
|
@ -79,6 +79,17 @@ class SetCookieTests(SimpleTestCase):
|
||||||
response.set_cookie('test', cookie_value)
|
response.set_cookie('test', cookie_value)
|
||||||
self.assertEqual(response.cookies['test'].value, cookie_value)
|
self.assertEqual(response.cookies['test'].value, cookie_value)
|
||||||
|
|
||||||
|
def test_samesite(self):
|
||||||
|
response = HttpResponse()
|
||||||
|
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".'):
|
||||||
|
HttpResponse().set_cookie('example', samesite='invalid')
|
||||||
|
|
||||||
|
|
||||||
class DeleteCookieTests(SimpleTestCase):
|
class DeleteCookieTests(SimpleTestCase):
|
||||||
|
|
||||||
|
|
|
@ -660,6 +660,16 @@ class SessionMiddlewareTests(TestCase):
|
||||||
str(response.cookies[settings.SESSION_COOKIE_NAME])
|
str(response.cookies[settings.SESSION_COOKIE_NAME])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@override_settings(SESSION_COOKIE_SAMESITE='Strict')
|
||||||
|
def test_samesite_session_cookie(self):
|
||||||
|
request = RequestFactory().get('/')
|
||||||
|
response = HttpResponse()
|
||||||
|
middleware = SessionMiddleware()
|
||||||
|
middleware.process_request(request)
|
||||||
|
request.session['hello'] = 'world'
|
||||||
|
response = middleware.process_response(request, response)
|
||||||
|
self.assertEqual(response.cookies[settings.SESSION_COOKIE_NAME]['samesite'], 'Strict')
|
||||||
|
|
||||||
@override_settings(SESSION_COOKIE_HTTPONLY=False)
|
@override_settings(SESSION_COOKIE_HTTPONLY=False)
|
||||||
def test_no_httponly_session_cookie(self):
|
def test_no_httponly_session_cookie(self):
|
||||||
request = RequestFactory().get('/')
|
request = RequestFactory().get('/')
|
||||||
|
|
Loading…
Reference in New Issue