Selaa lähdekoodia

Fixed #22185 -- Added settings.CSRF_COOKIE_AGE

Thanks Paul McMillan for the review.
Roger Hu 11 vuotta sitten
vanhempi
commit
9b729ddd8f

+ 1 - 0
django/conf/global_settings.py

@@ -558,6 +558,7 @@ CSRF_FAILURE_VIEW = 'django.views.csrf.csrf_failure'
 
 # Settings for CSRF cookie.
 CSRF_COOKIE_NAME = 'csrftoken'
+CSRF_COOKIE_AGE = 60 * 60 * 24 * 7 * 52
 CSRF_COOKIE_DOMAIN = None
 CSRF_COOKIE_PATH = '/'
 CSRF_COOKIE_SECURE = False

+ 1 - 1
django/middleware/csrf.py

@@ -196,7 +196,7 @@ class CsrfViewMiddleware(object):
         # the expiry timer.
         response.set_cookie(settings.CSRF_COOKIE_NAME,
                             request.META["CSRF_COOKIE"],
-                            max_age=60 * 60 * 24 * 7 * 52,
+                            max_age=settings.CSRF_COOKIE_AGE,
                             domain=settings.CSRF_COOKIE_DOMAIN,
                             path=settings.CSRF_COOKIE_PATH,
                             secure=settings.CSRF_COOKIE_SECURE,

+ 1 - 0
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_AGE`
 * :setting:`CSRF_COOKIE_DOMAIN`
 * :setting:`CSRF_COOKIE_HTTPONLY`
 * :setting:`CSRF_COOKIE_NAME`

+ 22 - 0
docs/ref/settings.txt

@@ -324,6 +324,28 @@ See :doc:`/topics/cache`.
 
 .. _settings-csrf:
 
+.. setting:: CSRF_COOKIE_AGE
+
+CSRF_COOKIE_AGE
+---------------
+
+.. versionadded:: 1.7
+
+Default: ``31449600`` (1 year, in seconds)
+
+The age of CSRF cookies, in seconds.
+
+The reason for setting a long-lived expiration time is to avoid problems in
+the case of a user closing a browser or bookmarking a page and then loading
+that page from a browser cache. Without persistent cookies, the form submission
+would fail in this case.
+
+Some browsers (specifically Internet Explorer) can disallow the use of
+persistent cookies or can have the indexes to the cookie jar corrupted on disk,
+thereby causing CSRF protection checks to fail (and sometimes intermittently).
+Change this setting to ``None`` to use session-based CSRF cookies, which
+keep the cookies in-memory instead of on persistent storage.
+
 .. setting:: CSRF_COOKIE_DOMAIN
 
 CSRF_COOKIE_DOMAIN

+ 6 - 0
docs/releases/1.7.txt

@@ -446,6 +446,12 @@ Cache
   "non-expiring" by default. Previously, it was only possible to pass
   ``timeout=None` to the cache backend's ``set()`` method.
 
+Cross Site Request Forgery
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The :setting:`CSRF_COOKIE_AGE` setting facilitates the use of session-based
+  CSRF cookies.
+
 Email
 ^^^^^
 

+ 44 - 0
tests/csrf_tests/tests.py

@@ -384,3 +384,47 @@ class CsrfViewMiddlewareTest(TestCase):
         finally:
             logger.removeHandler(test_handler)
             logger.setLevel(old_log_level)
+
+    def test_csrf_cookie_age(self):
+        """
+        Test to verify CSRF cookie age can be set using
+        settings.CSRF_COOKIE_AGE.
+        """
+        req = self._get_GET_no_csrf_cookie_request()
+
+        MAX_AGE = 123
+        with self.settings(CSRF_COOKIE_NAME='csrfcookie',
+                           CSRF_COOKIE_DOMAIN='.example.com',
+                           CSRF_COOKIE_AGE=MAX_AGE,
+                           CSRF_COOKIE_PATH='/test/',
+                           CSRF_COOKIE_SECURE=True,
+                           CSRF_COOKIE_HTTPONLY=True):
+            # token_view calls get_token() indirectly
+            CsrfViewMiddleware().process_view(req, token_view, (), {})
+            resp = token_view(req)
+
+            resp2 = CsrfViewMiddleware().process_response(req, resp)
+            max_age = resp2.cookies.get('csrfcookie').get('max-age')
+            self.assertEqual(max_age, MAX_AGE)
+
+    def test_csrf_cookie_age_none(self):
+        """
+        Test to verify CSRF cookie age does not have max age set and therefore
+        uses session-based cookies.
+        """
+        req = self._get_GET_no_csrf_cookie_request()
+
+        MAX_AGE = None
+        with self.settings(CSRF_COOKIE_NAME='csrfcookie',
+                           CSRF_COOKIE_DOMAIN='.example.com',
+                           CSRF_COOKIE_AGE=MAX_AGE,
+                           CSRF_COOKIE_PATH='/test/',
+                           CSRF_COOKIE_SECURE=True,
+                           CSRF_COOKIE_HTTPONLY=True):
+            # token_view calls get_token() indirectly
+            CsrfViewMiddleware().process_view(req, token_view, (), {})
+            resp = token_view(req)
+
+            resp2 = CsrfViewMiddleware().process_response(req, resp)
+            max_age = resp2.cookies.get('csrfcookie').get('max-age')
+            self.assertEqual(max_age, '')