Przeglądaj źródła

Fixed #32817 -- Added the token source to CsrfViewMiddleware's bad token error messages.

Chris Jerdonek 3 lat temu
rodzic
commit
fcb75651f9
2 zmienionych plików z 37 dodań i 10 usunięć
  1. 15 3
      django/middleware/csrf.py
  2. 22 7
      tests/csrf_tests/tests.py

+ 15 - 3
django/middleware/csrf.py

@@ -11,6 +11,7 @@ from urllib.parse import urlparse
 
 from django.conf import settings
 from django.core.exceptions import DisallowedHost, ImproperlyConfigured
+from django.http.request import HttpHeaders
 from django.urls import get_callable
 from django.utils.cache import patch_vary_headers
 from django.utils.crypto import constant_time_compare, get_random_string
@@ -28,7 +29,6 @@ REASON_BAD_ORIGIN = "Origin checking failed - %s does not match any trusted orig
 REASON_NO_REFERER = "Referer checking failed - no Referer."
 REASON_BAD_REFERER = "Referer checking failed - %s does not match any trusted origins."
 REASON_NO_CSRF_COOKIE = "CSRF cookie not set."
-REASON_CSRF_TOKEN_INCORRECT = 'CSRF token incorrect.'
 REASON_CSRF_TOKEN_MISSING = 'CSRF token missing.'
 REASON_MALFORMED_REFERER = "Referer checking failed - Referer is malformed."
 REASON_INSECURE_REFERER = "Referer checking failed - Referer is insecure while host is secure."
@@ -315,6 +315,13 @@ class CsrfViewMiddleware(MiddlewareMixin):
         if not is_same_domain(referer.netloc, good_referer):
             raise RejectRequest(REASON_BAD_REFERER % referer.geturl())
 
+    def _bad_token_message(self, reason, token_source):
+        if token_source != 'POST':
+            # Assume it is a settings.CSRF_HEADER_NAME value.
+            header_name = HttpHeaders.parse_header_name(token_source)
+            token_source = f'the {header_name!r} HTTP header'
+        return f'CSRF token from {token_source} {reason}.'
+
     def _check_token(self, request):
         # Access csrf_token via self._get_token() as rotate_token() may have
         # been called by an authentication middleware during the
@@ -349,14 +356,19 @@ class CsrfViewMiddleware(MiddlewareMixin):
                 request_csrf_token = request.META[settings.CSRF_HEADER_NAME]
             except KeyError:
                 raise RejectRequest(REASON_CSRF_TOKEN_MISSING)
+            token_source = settings.CSRF_HEADER_NAME
+        else:
+            token_source = 'POST'
 
         try:
             request_csrf_token = _sanitize_token(request_csrf_token)
         except InvalidTokenFormat as exc:
-            raise RejectRequest(f'CSRF token {exc.reason}.')
+            reason = self._bad_token_message(exc.reason, token_source)
+            raise RejectRequest(reason)
 
         if not _compare_masked_tokens(request_csrf_token, csrf_token):
-            raise RejectRequest(REASON_CSRF_TOKEN_INCORRECT)
+            reason = self._bad_token_message('incorrect', token_source)
+            raise RejectRequest(reason)
 
     def process_request(self, request):
         try:

+ 22 - 7
tests/csrf_tests/tests.py

@@ -147,12 +147,24 @@ class CsrfViewMiddlewareTestMixin:
         """
         cases = [
             (None, None, REASON_CSRF_TOKEN_MISSING),
-            (16 * 'a', None, 'CSRF token has incorrect length.'),
-            (64 * '*', None, 'CSRF token has invalid characters.'),
-            (64 * 'a', None, 'CSRF token incorrect.'),
-            (None, 16 * 'a', 'CSRF token has incorrect length.'),
-            (None, 64 * '*', 'CSRF token has invalid characters.'),
-            (None, 64 * 'a', 'CSRF token incorrect.'),
+            (16 * 'a', None, 'CSRF token from POST has incorrect length.'),
+            (64 * '*', None, 'CSRF token from POST has invalid characters.'),
+            (64 * 'a', None, 'CSRF token from POST incorrect.'),
+            (
+                None,
+                16 * 'a',
+                "CSRF token from the 'X-Csrftoken' HTTP header has incorrect length.",
+            ),
+            (
+                None,
+                64 * '*',
+                "CSRF token from the 'X-Csrftoken' HTTP header has invalid characters.",
+            ),
+            (
+                None,
+                64 * 'a',
+                "CSRF token from the 'X-Csrftoken' HTTP header incorrect.",
+            ),
         ]
         for post_token, meta_token, expected in cases:
             with self.subTest(post_token=post_token, meta_token=meta_token):
@@ -168,7 +180,10 @@ class CsrfViewMiddlewareTestMixin:
         If a CSRF cookie is present and an invalid token is passed via a
         custom CSRF_HEADER_NAME, the middleware rejects the incoming request.
         """
-        expected = 'CSRF token has incorrect length.'
+        expected = (
+            "CSRF token from the 'X-Csrftoken-Customized' HTTP header has "
+            "incorrect length."
+        )
         self._check_bad_or_missing_token(
             expected,
             meta_token=16 * 'a',