From 8cbcf1d3a60a0ba1a6f3ddde9317ac07b67c6c5d Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Mon, 9 May 2011 23:00:22 +0000 Subject: [PATCH] Fixed #14134 - ability to set cookie 'path' and 'secure' attributes of CSRF cookie Thanks to cfattarsi for the report and initial patch. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16200 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/conf/global_settings.py | 4 +++- django/middleware/csrf.py | 8 +++++-- docs/ref/contrib/csrf.txt | 25 +++++++++++++++++++ docs/ref/settings.txt | 29 +++++++++++++++++++++++ tests/regressiontests/csrf_tests/tests.py | 18 ++++++++++---- 5 files changed, 76 insertions(+), 8 deletions(-) diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 477f13b675..88aa5a3e70 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -484,9 +484,11 @@ PASSWORD_RESET_TIMEOUT_DAYS = 3 # rejected by the CSRF middleware. CSRF_FAILURE_VIEW = 'django.views.csrf.csrf_failure' -# Name and domain for CSRF cookie. +# Settings for CSRF cookie. CSRF_COOKIE_NAME = 'csrftoken' CSRF_COOKIE_DOMAIN = None +CSRF_COOKIE_PATH = '/' +CSRF_COOKIE_SECURE = False ############ # MESSAGES # diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index 2f36f18e6c..e4cab6c08f 100644 --- a/django/middleware/csrf.py +++ b/django/middleware/csrf.py @@ -197,8 +197,12 @@ class CsrfViewMiddleware(object): # Set the CSRF cookie even if it's already set, so we renew the expiry timer. response.set_cookie(settings.CSRF_COOKIE_NAME, - request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52, - domain=settings.CSRF_COOKIE_DOMAIN) + request.META["CSRF_COOKIE"], + max_age = 60 * 60 * 24 * 7 * 52, + domain=settings.CSRF_COOKIE_DOMAIN, + path=settings.CSRF_COOKIE_PATH, + secure=settings.CSRF_COOKIE_SECURE + ) # Content varies with the CSRF cookie, so set the Vary header. patch_vary_headers(response, ('Cookie',)) response.csrf_processing_done = True diff --git a/docs/ref/contrib/csrf.txt b/docs/ref/contrib/csrf.txt index 88e9523719..b42dc26fbd 100644 --- a/docs/ref/contrib/csrf.txt +++ b/docs/ref/contrib/csrf.txt @@ -423,6 +423,31 @@ Default: ``'csrftoken'`` The name of the cookie to use for the CSRF authentication token. This can be whatever you want. +CSRF_COOKIE_PATH +---------------- + +.. versionadded:: 1.4 + +Default: ``'/'`` + +The path set on the CSRF cookie. This should either match the URL path of your +Django installation or be a parent of that path. + +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 +its own CSRF cookie. + +CSRF_COOKIE_SECURE +------------------ + +.. versionadded:: 1.4 + +Default: ``False`` + +Whether to use a secure cookie for the CSRF cookie. If this is set to ``True``, +the cookie will be marked as "secure," which means browsers may ensure that the +cookie is only sent under an HTTPS connection. + CSRF_FAILURE_VIEW ----------------- diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index f8a5e0f640..f5f1226f21 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -340,6 +340,35 @@ Default: ``'csrftoken'`` The name of the cookie to use for the CSRF authentication token. This can be whatever you want. See :doc:`/ref/contrib/csrf`. +.. setting:: CSRF_COOKIE_PATH + +CSRF_COOKIE_PATH +---------------- + +.. versionadded:: 1.4 + +Default: ``'/'`` + +The path set on the CSRF cookie. This should either match the URL path of your +Django installation or be a parent of that path. + +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 +its own CSRF cookie. + +.. setting:: CSRF_COOKIE_SECURE + +CSRF_COOKIE_SECURE +------------------ + +.. versionadded:: 1.4 + +Default: ``False`` + +Whether to use a secure cookie for the CSRF cookie. If this is set to ``True``, +the cookie will be marked as "secure," which means browsers may ensure that the +cookie is only sent under an HTTPS connection. + .. setting:: CSRF_FAILURE_VIEW CSRF_FAILURE_VIEW diff --git a/tests/regressiontests/csrf_tests/tests.py b/tests/regressiontests/csrf_tests/tests.py index 1285c1ddac..8dc3fbbc1b 100644 --- a/tests/regressiontests/csrf_tests/tests.py +++ b/tests/regressiontests/csrf_tests/tests.py @@ -82,13 +82,21 @@ class CsrfViewMiddlewareTest(TestCase): patched. """ req = self._get_GET_no_csrf_cookie_request() - # token_view calls get_token() indirectly - CsrfViewMiddleware().process_view(req, token_view, (), {}) - resp = token_view(req) - resp2 = CsrfViewMiddleware().process_response(req, resp) - csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False) + # Put tests for CSRF_COOKIE_* settings here + with self.settings(CSRF_COOKIE_NAME='myname', + CSRF_COOKIE_DOMAIN='.example.com', + CSRF_COOKIE_PATH='/test/', + CSRF_COOKIE_SECURE=True): + # token_view calls get_token() indirectly + CsrfViewMiddleware().process_view(req, token_view, (), {}) + resp = token_view(req) + resp2 = CsrfViewMiddleware().process_response(req, resp) + csrf_cookie = resp2.cookies.get('myname', False) self.assertNotEqual(csrf_cookie, False) + self.assertEqual(csrf_cookie['domain'], '.example.com') + self.assertEqual(csrf_cookie['secure'], True) + self.assertEqual(csrf_cookie['path'], '/test/') self.assertTrue('Cookie' in resp2.get('Vary','')) def test_process_response_get_token_not_used(self):