diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 740c792dcf..6a01493a72 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -529,6 +529,7 @@ CSRF_COOKIE_NAME = 'csrftoken' CSRF_COOKIE_DOMAIN = None CSRF_COOKIE_PATH = '/' CSRF_COOKIE_SECURE = False +CSRF_COOKIE_HTTPONLY = False ############ # MESSAGES # diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index 339f42a110..423034478b 100644 --- a/django/middleware/csrf.py +++ b/django/middleware/csrf.py @@ -210,7 +210,8 @@ class CsrfViewMiddleware(object): max_age = 60 * 60 * 24 * 7 * 52, domain=settings.CSRF_COOKIE_DOMAIN, path=settings.CSRF_COOKIE_PATH, - secure=settings.CSRF_COOKIE_SECURE + secure=settings.CSRF_COOKIE_SECURE, + httponly=settings.CSRF_COOKIE_HTTPONLY ) # Content varies with the CSRF cookie, so set the Vary header. patch_vary_headers(response, ('Cookie',)) diff --git a/docs/ref/contrib/csrf.txt b/docs/ref/contrib/csrf.txt index 3ad16e2f97..14522d8dbc 100644 --- a/docs/ref/contrib/csrf.txt +++ b/docs/ref/contrib/csrf.txt @@ -491,6 +491,7 @@ Settings A number of settings can be used to control Django's CSRF behavior: * :setting:`CSRF_COOKIE_DOMAIN` +* :setting:`CSRF_COOKIE_HTTPONLY` * :setting:`CSRF_COOKIE_NAME` * :setting:`CSRF_COOKIE_PATH` * :setting:`CSRF_COOKIE_SECURE` diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index d4c6868ced..9a615b2d99 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -281,6 +281,19 @@ Please note that the presence of this setting does not imply that Django's CSRF protection is safe from cross-subdomain attacks by default - please see the :ref:`CSRF limitations ` section. +.. setting:: CSRF_COOKIE_HTTPONLY + +CSRF_COOKIE_HTTPONLY +-------------------- + +.. versionadded:: 1.6 + +Default: ``False`` + +Whether to use HttpOnly flag on the CSRF cookie. If this is set to ``True``, +client-side JavaScript will not to be able to access the CSRF cookie. See +:setting:`SESSION_COOKIE_HTTPONLY` for details on HttpOnly. + .. setting:: CSRF_COOKIE_NAME CSRF_COOKIE_NAME diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index f86d8b8108..f53fa8ac4c 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -36,6 +36,9 @@ Minor features * Authentication backends can raise ``PermissionDenied`` to immediately fail the authentication chain. +* The HttpOnly flag can be set on the CSRF cookie with + :setting:`CSRF_COOKIE_HTTPONLY`. + * The ``assertQuerysetEqual()`` now checks for undefined order and raises ``ValueError`` if undefined order is spotted. The order is seen as undefined if the given ``QuerySet`` isn't ordered and there are more than diff --git a/tests/regressiontests/csrf_tests/tests.py b/tests/regressiontests/csrf_tests/tests.py index c5b66a31d1..3719108962 100644 --- a/tests/regressiontests/csrf_tests/tests.py +++ b/tests/regressiontests/csrf_tests/tests.py @@ -101,7 +101,8 @@ class CsrfViewMiddlewareTest(TestCase): with self.settings(CSRF_COOKIE_NAME='myname', CSRF_COOKIE_DOMAIN='.example.com', CSRF_COOKIE_PATH='/test/', - CSRF_COOKIE_SECURE=True): + CSRF_COOKIE_SECURE=True, + CSRF_COOKIE_HTTPONLY=True): # token_view calls get_token() indirectly CsrfViewMiddleware().process_view(req, token_view, (), {}) resp = token_view(req) @@ -110,6 +111,7 @@ class CsrfViewMiddlewareTest(TestCase): self.assertNotEqual(csrf_cookie, False) self.assertEqual(csrf_cookie['domain'], '.example.com') self.assertEqual(csrf_cookie['secure'], True) + self.assertEqual(csrf_cookie['httponly'], True) self.assertEqual(csrf_cookie['path'], '/test/') self.assertTrue('Cookie' in resp2.get('Vary',''))