diff options
| author | Chris Jerdonek <chris.jerdonek@gmail.com> | 2021-04-03 02:02:47 -0700 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2021-05-28 07:32:01 +0200 |
| commit | 214b36f50aec5ff42776ba847ce33ce08d82ca76 (patch) | |
| tree | b7fa661e821b96fa1a701bf0c5e094e4371b822c | |
| parent | cfd8c918390cd5317621124d224a009196f8755c (diff) | |
Refs #32596 -- Added early return on safe methods in CsrfViewMiddleware.process_view().
| -rw-r--r-- | django/middleware/csrf.py | 127 |
1 files changed, 63 insertions, 64 deletions
diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index 18af1d619a..d862006a07 100644 --- a/django/middleware/csrf.py +++ b/django/middleware/csrf.py @@ -317,75 +317,74 @@ class CsrfViewMiddleware(MiddlewareMixin): return None # Assume that anything not defined as 'safe' by RFC7231 needs protection - if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): - if getattr(request, '_dont_enforce_csrf_checks', False): - # Mechanism to turn off CSRF checks for test suite. - # It comes after the creation of CSRF cookies, so that - # everything else continues to work exactly the same - # (e.g. cookies are sent, etc.), but before any - # branches that call reject(). - return self._accept(request) + if request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): + return self._accept(request) - # Reject the request if the Origin header doesn't match an allowed - # value. - if 'HTTP_ORIGIN' in request.META: - if not self._origin_verified(request): - return self._reject(request, REASON_BAD_ORIGIN % request.META['HTTP_ORIGIN']) - elif request.is_secure(): - # If the Origin header wasn't provided, reject HTTPS requests - # if the Referer header doesn't match an allowed value. - # - # Suppose user visits http://example.com/ - # An active network attacker (man-in-the-middle, MITM) sends a - # POST form that targets https://example.com/detonate-bomb/ and - # submits it via JavaScript. - # - # The attacker will need to provide a CSRF cookie and token, but - # that's no problem for a MITM and the session-independent - # secret we're using. So the MITM can circumvent the CSRF - # protection. This is true for any HTTP connection, but anyone - # using HTTPS expects better! For this reason, for - # https://example.com/ we need additional protection that treats - # http://example.com/ as completely untrusted. Under HTTPS, - # Barth et al. found that the Referer header is missing for - # same-domain requests in only about 0.2% of cases or less, so - # we can use strict Referer checking. - try: - self._check_referer(request) - except RejectRequest as exc: - return self._reject(request, exc.reason) + if getattr(request, '_dont_enforce_csrf_checks', False): + # Mechanism to turn off CSRF checks for test suite. It comes after + # the creation of CSRF cookies, so that everything else continues + # to work exactly the same (e.g. cookies are sent, etc.), but + # before any branches that call reject(). + return self._accept(request) - # Access csrf_token via self._get_token() as rotate_token() may - # have been called by an authentication middleware during the - # process_request() phase. - csrf_token = self._get_token(request) - if csrf_token is None: - # No CSRF cookie. For POST requests, we insist on a CSRF cookie, - # and in this way we can avoid all CSRF attacks, including login - # CSRF. - return self._reject(request, REASON_NO_CSRF_COOKIE) + # Reject the request if the Origin header doesn't match an allowed + # value. + if 'HTTP_ORIGIN' in request.META: + if not self._origin_verified(request): + return self._reject(request, REASON_BAD_ORIGIN % request.META['HTTP_ORIGIN']) + elif request.is_secure(): + # If the Origin header wasn't provided, reject HTTPS requests if + # the Referer header doesn't match an allowed value. + # + # Suppose user visits http://example.com/ + # An active network attacker (man-in-the-middle, MITM) sends a + # POST form that targets https://example.com/detonate-bomb/ and + # submits it via JavaScript. + # + # The attacker will need to provide a CSRF cookie and token, but + # that's no problem for a MITM and the session-independent secret + # we're using. So the MITM can circumvent the CSRF protection. This + # is true for any HTTP connection, but anyone using HTTPS expects + # better! For this reason, for https://example.com/ we need + # additional protection that treats http://example.com/ as + # completely untrusted. Under HTTPS, Barth et al. found that the + # Referer header is missing for same-domain requests in only about + # 0.2% of cases or less, so we can use strict Referer checking. + try: + self._check_referer(request) + except RejectRequest as exc: + return self._reject(request, exc.reason) + + # Access csrf_token via self._get_token() as rotate_token() may have + # been called by an authentication middleware during the + # process_request() phase. + csrf_token = self._get_token(request) + if csrf_token is None: + # No CSRF cookie. For POST requests, we insist on a CSRF cookie, + # and in this way we can avoid all CSRF attacks, including login + # CSRF. + return self._reject(request, REASON_NO_CSRF_COOKIE) - # Check non-cookie token for match. - request_csrf_token = "" - if request.method == "POST": - try: - request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') - except OSError: - # Handle a broken connection before we've completed reading - # the POST data. process_view shouldn't raise any - # exceptions, so we'll ignore and serve the user a 403 - # (assuming they're still listening, which they probably - # aren't because of the error). - pass + # Check non-cookie token for match. + request_csrf_token = '' + if request.method == 'POST': + try: + request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') + except OSError: + # Handle a broken connection before we've completed reading the + # POST data. process_view shouldn't raise any exceptions, so + # we'll ignore and serve the user a 403 (assuming they're still + # listening, which they probably aren't because of the error). + pass - if request_csrf_token == "": - # Fall back to X-CSRFToken, to make things easier for AJAX, - # and possible for PUT/DELETE. - request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '') + if request_csrf_token == '': + # Fall back to X-CSRFToken, to make things easier for AJAX, and + # possible for PUT/DELETE. + request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '') - request_csrf_token = _sanitize_token(request_csrf_token) - if not _compare_masked_tokens(request_csrf_token, csrf_token): - return self._reject(request, REASON_BAD_TOKEN) + request_csrf_token = _sanitize_token(request_csrf_token) + if not _compare_masked_tokens(request_csrf_token, csrf_token): + return self._reject(request, REASON_BAD_TOKEN) return self._accept(request) |
