def _origin_verified(self, request): request_origin = request.META['HTTP_ORIGIN'] try: good_host = request.get_host() except DisallowedHost: pass else: good_origin = '%s://%s' % ( 'https' if request.is_secure() else 'http', good_host, ) if request_origin == good_origin: return True if request_origin in self.allowed_origins_exact: return True try: parsed_origin = urlparse(request_origin) except ValueError: return False request_scheme = parsed_origin.scheme request_netloc = parsed_origin.netloc return any( is_same_domain(request_netloc, host) for host in self.allowed_origin_subdomains.get(request_scheme, ()) )
def test_is_same_domain_bad(self): for pair in ( ('example2.com', 'example.com'), ('foo.example.com', 'example.com'), ('example.com:9999', 'example.com:8888'), ): self.assertFalse(http.is_same_domain(*pair))
def test_is_same_domain_bad(self): for pair in ( ("example2.com", "example.com"), ("foo.example.com", "example.com"), ("example.com:9999", "example.com:8888"), ): self.assertFalse(http.is_same_domain(*pair))
def test_bad(self): for pair in ( ('example2.com', 'example.com'), ('foo.example.com', 'example.com'), ('example.com:9999', 'example.com:8888'), ): self.assertIs(is_same_domain(*pair), False)
def check_referrer(request) -> Optional[str]: """ Returns None if the check is successful, or a reason to reject the request. 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. """ if request.scheme != 'https': return referer = request.headers.get('Referrer') if referer is None: return REASON_NO_REFERER referer = urlparse(referer) # Make sure we have a valid URL for Referer. if '' in (referer.scheme, referer.netloc): return REASON_MALFORMED_REFERER # Ensure that our Referer is also secure. (if we ourselves use HTTPS) if referer.scheme != 'https' and request.scheme == 'https': return REASON_INSECURE_REFERER # If there isn't a CSRF_COOKIE_DOMAIN, require an exact match # match on host:port. If not, obey the cookie rules (or those # for the session cookie, if CSRF_USE_SESSIONS). good_referer = settings.SESSION_COOKIE_DOMAIN # using django's var because it suits us if good_referer is not None: server_port = request.port if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) else: good_referer = request.host # Create a list of all acceptable HTTP referers, including the # current host if it's permitted by ALLOWED_HOSTS. good_hosts = list(settings.API_CSRF_TRUSTED_ORIGINS) if good_referer is not None: good_hosts.append(good_referer) if not any( is_same_domain(referer.netloc, host) for host in good_hosts): reason = REASON_BAD_REFERER % referer.geturl() return reason
def test_bad(self): for pair in ( ("example2.com", "example.com"), ("foo.example.com", "example.com"), ("example.com:9999", "example.com:8888"), ("foo.example.com:8888", ""), ): self.assertIs(is_same_domain(*pair), False)
def _is_host_included(self, host): """ Mirrors the behavior of django.http.request.validate_host(), but does not match '*' (which would exclude all hosts). To exclude all requests from being processed by LocaleMiddleware one should simply remove this class from settings.MIDDLEWARE. """ domain, _ = split_domain_port(host) return not any(is_same_domain(domain, pattern) for pattern in self._excluded_hosts)
def _check_referer(self, request): referer = request.META.get('HTTP_REFERER') if referer is None: raise RejectRequest(REASON_NO_REFERER) try: referer = urlparse(referer) except ValueError: raise RejectRequest(REASON_MALFORMED_REFERER) # Make sure we have a valid URL for Referer. if '' in (referer.scheme, referer.netloc): raise RejectRequest(REASON_MALFORMED_REFERER) # Ensure that our Referer is also secure. if referer.scheme != 'https': raise RejectRequest(REASON_INSECURE_REFERER) if any( is_same_domain(referer.netloc, host) for host in self.csrf_trusted_origins_hosts ): return # Allow matching the configured cookie domain. good_referer = ( settings.SESSION_COOKIE_DOMAIN if settings.CSRF_USE_SESSIONS else settings.CSRF_COOKIE_DOMAIN ) if good_referer is None: # If no cookie domain is configured, allow matching the current # host:port exactly if it's permitted by ALLOWED_HOSTS. try: # request.get_host() includes the port. good_referer = request.get_host() except DisallowedHost: raise RejectRequest(REASON_BAD_REFERER % referer.geturl()) else: server_port = request.get_port() if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) if not is_same_domain(referer.netloc, good_referer): raise RejectRequest(REASON_BAD_REFERER % referer.geturl())
def test_good(self): for pair in ( ('example.com', 'example.com'), ('example.com', '.example.com'), ('foo.example.com', '.example.com'), ('example.com:8888', 'example.com:8888'), ('example.com:8888', '.example.com:8888'), ('foo.example.com:8888', '.example.com:8888'), ): self.assertIs(is_same_domain(*pair), True)
def test_is_same_domain_good(self): for pair in ( ('example.com', 'example.com'), ('example.com', '.example.com'), ('foo.example.com', '.example.com'), ('example.com:8888', 'example.com:8888'), ('example.com:8888', '.example.com:8888'), ('foo.example.com:8888', '.example.com:8888'), ): self.assertTrue(http.is_same_domain(*pair))
def test_is_same_domain_good(self): for pair in ( ("example.com", "example.com"), ("example.com", ".example.com"), ("foo.example.com", ".example.com"), ("example.com:8888", "example.com:8888"), ("example.com:8888", ".example.com:8888"), ("foo.example.com:8888", ".example.com:8888"), ): self.assertTrue(http.is_same_domain(*pair))
def test_good(self): for pair in ( ("example.com", "example.com"), ("example.com", ".example.com"), ("foo.example.com", ".example.com"), ("example.com:8888", "example.com:8888"), ("example.com:8888", ".example.com:8888"), ("foo.example.com:8888", ".example.com:8888"), ): self.assertIs(is_same_domain(*pair), True)
def _is_host_included(self, host): """ Mirrors the behavior of django.http.request.validate_host(), but does not match '*' (which would exclude all hosts). To exclude all requests from being processed by LocaleMiddleware one should simply remove this class from settings.MIDDLEWARE. """ domain, _ = split_domain_port(host) return not any( is_same_domain(domain, pattern) for pattern in self._excluded_hosts)
def get_matching_allowed_host(host): """Find the first host in ALLOWED_HOSTS that matches the provided host.""" host, port = split_domain_port(host) host = host[:-1] if host.endswith('.') else host allowed_hosts = getattr(settings, 'ALLOWED_HOSTS', []) for pattern in allowed_hosts: if is_same_domain(host, pattern): return pattern return None
def _django_csrf_check(self, request): ''' Taken from django.middleware.csrf: @see django.middleware.csrf ''' referer = force_text( request.META.get('HTTP_REFERER'), strings_only=True, errors='replace' ) if referer is None: logger.error('csrf reject: %s', REASON_NO_REFERER) return False from django.utils.six.moves.urllib.parse import urlparse referer = urlparse(referer) # Make sure we have a valid URL for Referer. if '' in (referer.scheme, referer.netloc): logger.error('csrf reject: %s', REASON_MALFORMED_REFERER) return False # Ensure that our Referer is also secure. if referer.scheme != 'https': logger.error('csrf reject: %s', REASON_INSECURE_REFERER) return False # If there isn't a CSRF_COOKIE_DOMAIN, assume we need an exact # match on host:port. If not, obey the cookie rules. if settings.CSRF_COOKIE_DOMAIN is None: # request.get_host() includes the port. good_referer = request.get_host() else: good_referer = settings.CSRF_COOKIE_DOMAIN server_port = request.get_port() if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) # Here we generate a list of all acceptable HTTP referers, # including the current host since that has been validated # upstream. good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) good_hosts.append(good_referer) if not any(is_same_domain(referer.netloc, host) for host in good_hosts): reason = REASON_BAD_REFERER % referer.geturl() logger.info('csrf reject: %r', reason) return False return True
def _django_csrf_check(self, request): ''' Taken from django.middleware.csrf: @see django.middleware.csrf ''' referer = force_text(request.META.get('HTTP_REFERER'), strings_only=True, errors='replace') if referer is None: logger.error('csrf reject: %s', REASON_NO_REFERER) return False from django.utils.six.moves.urllib.parse import urlparse referer = urlparse(referer) # Make sure we have a valid URL for Referer. if '' in (referer.scheme, referer.netloc): logger.error('csrf reject: %s', REASON_MALFORMED_REFERER) return False # Ensure that our Referer is also secure. if referer.scheme != 'https': logger.error('csrf reject: %s', REASON_INSECURE_REFERER) return False # If there isn't a CSRF_COOKIE_DOMAIN, assume we need an exact # match on host:port. If not, obey the cookie rules. if settings.CSRF_COOKIE_DOMAIN is None: # request.get_host() includes the port. good_referer = request.get_host() else: good_referer = settings.CSRF_COOKIE_DOMAIN server_port = request.get_port() if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) # Here we generate a list of all acceptable HTTP referers, # including the current host since that has been validated # upstream. good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) good_hosts.append(good_referer) if not any( is_same_domain(referer.netloc, host) for host in good_hosts): reason = REASON_BAD_REFERER % referer.geturl() logger.info('csrf reject: %r', reason) return False return True
def validate_host(host, allowed_hosts): """ Validate the given host for this site. Check that the host looks valid and matches a host or host pattern in the given list of ``allowed_hosts``. Any pattern beginning with a period matches a domain and all its subdomains (e.g. ``.example.com`` matches ``example.com`` and any subdomain), ``*`` matches anything, and anything else must match exactly. Note: This function assumes that the given host is lower-cased and has already had the port, if any, stripped off. Return ``True`` for a valid host, ``False`` otherwise. """ return any(pattern == '*' or is_same_domain(host, pattern) for pattern in allowed_hosts)
def is_first_party_link(url, *, first_party_hosts=None): """ Return whether an URL is a first-party link or not. First parties are defined by ``ALLOWED_HOSTS`` and can be overridden by passing an alternative list of hosts. The wildcard ``["*"]`` isn't recognized. """ u = urlparse(url) if u.scheme and u.scheme not in {"http", "https"}: return False if not u.hostname: return True hosts = settings.ALLOWED_HOSTS if first_party_hosts is None else first_party_hosts return any(is_same_domain(u.hostname, pattern) for pattern in hosts)
def _referer_tarkistus(self, request): ''' Tarkista Referer-otsake. Ks. CsrfViewMiddleware.process_view: lohko, joka alkaa samoin kuin tämä metodi. Huomaa, että ainoastaan WSS-pyyntöjen Referer tarkistetaan; WS hyväksytään ehdoitta. ''' if request.is_secure(): referer = request.META.get('HTTP_REFERER') if referer is None: return False referer = urlparse(referer) if '' in (referer.scheme, referer.netloc): return False if referer.scheme != 'https': return False good_referer = (settings.SESSION_COOKIE_DOMAIN if settings.CSRF_USE_SESSIONS else settings.CSRF_COOKIE_DOMAIN) if good_referer is not None: server_port = request.get_port() if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) else: try: good_referer = request.get_host() except DisallowedHost: pass good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) if good_referer is not None: good_hosts.append(good_referer) if not any( is_same_domain(referer.netloc, host) for host in good_hosts): return False return True
def process_view(self, request, callback, callback_args, callback_kwargs): print('process view!!!!') if getattr(request, 'csrf_processing_done', False): print( "if getattr(request, 'csrf_processing_done', False): === True") return None # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works if getattr(callback, 'csrf_exempt', False): res = getattr(callback, 'csrf_exempt', False) print("if getattr(callback, 'csrf_exempt', False): " + str(res)) print('callback ' + str(callback)) 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.is_secure(): # 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. referer = request.META.get('HTTP_REFERER') print("request.META.get('HTTP_REFERER')" + str(referer)) if referer is None: return self._reject(request, REASON_NO_REFERER) referer = urlparse(referer) print("urlparse(referer) " + str(referer)) print("'' in (referer.scheme, referer.netloc) " + str('' in (referer.scheme, referer.netloc))) # Make sure we have a valid URL for Referer. if '' in (referer.scheme, referer.netloc): return self._reject(request, REASON_MALFORMED_REFERER) print("referer.scheme != 'https' " + str(referer.scheme != 'https')) # Ensure that our Referer is also secure. if referer.scheme != 'https': return self._reject(request, REASON_INSECURE_REFERER) # If there isn't a CSRF_COOKIE_DOMAIN, require an exact match # match on host:port. If not, obey the cookie rules (or those # for the session cookie, if CSRF_USE_SESSIONS). good_referer = (settings.SESSION_COOKIE_DOMAIN if settings.CSRF_USE_SESSIONS else settings.CSRF_COOKIE_DOMAIN) print("good_referer = (... " + str(good_referer)) if good_referer is not None: server_port = request.get_port() print("server_port = request.get_port() " + str(server_port)) if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) print( "good_referer = '%s:%s' % (good_referer, server_port) " + str(good_referer)) else: try: # request.get_host() includes the port. good_referer = request.get_host() print("good_referer = request.get_host() " + str(good_referer)) except DisallowedHost: pass # Create a list of all acceptable HTTP referers, including the # current host if it's permitted by ALLOWED_HOSTS. good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) print("good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) " + str(good_hosts)) if good_referer is not None: good_hosts.append(good_referer) print( "not any(is_same_domain(referer.netloc, host) for host in good_hosts) " + str(not any( is_same_domain(referer.netloc, host) for host in good_hosts))) if not any( is_same_domain(referer.netloc, host) for host in good_hosts): reason = REASON_BAD_REFERER % referer.geturl() return self._reject(request, reason) csrf_token = request.META.get('CSRF_COOKIE') print("csrf_token = request.META.get('CSRF_COOKIE') " + str(csrf_token)) 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. print('return self._reject(request, REASON_NO_CSRF_COOKIE)') 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', '') print( "request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') " + str(request_csrf_token)) 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 except Exception as e: print(e) raise e # print('\n'*5) # print('CSRF NONSENSE from the perspective of csrf.py!!!!!!!!!') # for key in request.META: # print(key, request.META[key]) # print(' END CSRF NONSENSE from the perspective of csrf.py!!!!!!!!!') # print('\n' * 5, flush=True) 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, '') print( "request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '') " + str(request_csrf_token)) request_csrf_token = _sanitize_token(request_csrf_token) print("request_csrf_token " + str(request_csrf_token), flush=True) if not _compare_salted_tokens(request_csrf_token, csrf_token): print('return self._reject(request, REASON_BAD_TOKEN)') return self._reject(request, REASON_BAD_TOKEN) print('return self._accept(request)') return self._accept(request)
def debug_request_for_csrf_issues(request): return from urllib.parse import urlparse from django.core.exceptions import DisallowedHost, ImproperlyConfigured from django.utils.http import is_same_domain def _sanitize_token(token): # Allow only ASCII alphanumerics return token print("getattr(request, 'csrf_processing_done', False) " + str(getattr(request, 'csrf_processing_done', False))) print("request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE') " + str( request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'))) if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): print("getattr(request, '_dont_enforce_csrf_checks', False) " + str( getattr(request, '_dont_enforce_csrf_checks', False))) if getattr(request, '_dont_enforce_csrf_checks', False): pass print("request.is_secure() " + str(request.is_secure())) if request.is_secure(): referer = request.META.get('HTTP_REFERER') print("request.META.get('HTTP_REFERER')" + str(referer)) referer = urlparse(referer) print("urlparse(referer) " + str(referer)) print("'' in (referer.scheme, referer.netloc) " + str('' in (referer.scheme, referer.netloc))) print("referer.scheme != 'https' " + str(referer.scheme != 'https')) good_referer = ( settings.SESSION_COOKIE_DOMAIN if settings.CSRF_USE_SESSIONS else settings.CSRF_COOKIE_DOMAIN ) print("good_referer = (... " + str(good_referer)) if good_referer is not None: server_port = request.get_port() print("server_port = request.get_port() " + str(server_port)) if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) print("good_referer = '%s:%s' % (good_referer, server_port) " + str(good_referer)) else: try: good_referer = request.get_host() print("good_referer = request.get_host() " + str(good_referer)) except DisallowedHost: pass good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) print("good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) " + str(good_hosts)) if good_referer is not None: good_hosts.append(good_referer) print("not any(is_same_domain(referer.netloc, host) for host in good_hosts) " + str( not any(is_same_domain(referer.netloc, host) for host in good_hosts))) csrf_token = request.META.get('CSRF_COOKIE') print("csrf_token = request.META.get('CSRF_COOKIE') " + str(csrf_token)) request_csrf_token = "" if request.method == "POST": try: request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') print("request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') " + str(request_csrf_token)) 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, '') print("request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '') " + str(request_csrf_token)) request_csrf_token = _sanitize_token(request_csrf_token) print("request_csrf_token " + str(request_csrf_token)) else: pass
def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(request, 'csrf_processing_done', False): return None # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works if getattr(callback, 'csrf_exempt', False): 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.is_secure(): # 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. referer = request.META.get('HTTP_REFERER') # -- Change from original here -- # # Only checks referer if it is present. # It's not a failure condition if the referer is not present. # Our site is HTTPS-only which does not need to rely on # referer checking. Above example does not apply. if referer is not None: referer = urlparse(referer) # Make sure we have a valid URL for Referer. if '' in (referer.scheme, referer.netloc): return self._reject(request, REASON_MALFORMED_REFERER) # Ensure that our Referer is also secure. if referer.scheme != 'https': return self._reject(request, REASON_INSECURE_REFERER) # If there isn't a CSRF_COOKIE_DOMAIN, require an exact match # match on host:port. If not, obey the cookie rules (or those # for the session cookie, if CSRF_USE_SESSIONS). good_referer = ( settings.SESSION_COOKIE_DOMAIN if settings.CSRF_USE_SESSIONS else settings.CSRF_COOKIE_DOMAIN ) if good_referer is not None: server_port = request.get_port() if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) else: try: # request.get_host() includes the port. good_referer = request.get_host() except DisallowedHost: pass # Create a list of all acceptable HTTP referers, including the # current host if it's permitted by ALLOWED_HOSTS. good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) if good_referer is not None: good_hosts.append(good_referer) if not any(is_same_domain(referer.netloc, host) for host in good_hosts): reason = REASON_BAD_REFERER % referer.geturl() return self._reject(request, reason) csrf_token = request.META.get('CSRF_COOKIE') 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 IOError: # 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, '') request_csrf_token = _sanitize_token(request_csrf_token) if not _compare_salted_tokens(request_csrf_token, csrf_token): return self._reject(request, REASON_BAD_TOKEN) return self._accept(request)
def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(request, "csrf_processing_done", False): return None try: csrf_token = _sanitize_token(request.COOKIES[settings.CSRF_COOKIE_NAME]) # Use same token next time request.META["CSRF_COOKIE"] = csrf_token except KeyError: csrf_token = None # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works if getattr(callback, "csrf_exempt", False): 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.is_secure(): # 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 # nonce 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. referer = force_text(request.META.get("HTTP_REFERER"), strings_only=True, errors="replace") if referer is None: return self._reject(request, REASON_NO_REFERER) referer = urlparse(referer) # Make sure we have a valid URL for Referer. if "" in (referer.scheme, referer.netloc): return self._reject(request, REASON_MALFORMED_REFERER) # Ensure that our Referer is also secure. if referer.scheme != "https": return self._reject(request, REASON_INSECURE_REFERER) # If there isn't a CSRF_COOKIE_DOMAIN, assume we need an exact # match on host:port. If not, obey the cookie rules. if settings.CSRF_COOKIE_DOMAIN is None: # request.get_host() includes the port. good_referer = request.get_host() else: good_referer = settings.CSRF_COOKIE_DOMAIN server_port = request.get_port() if server_port not in ("443", "80"): good_referer = "%s:%s" % (good_referer, server_port) # Here we generate a list of all acceptable HTTP referers, # including the current host since that has been validated # upstream. good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) good_hosts.append(good_referer) if not any(is_same_domain(referer.netloc, host) for host in good_hosts): reason = REASON_BAD_REFERER % referer.geturl() return self._reject(request, reason) 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 IOError: # 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 not constant_time_compare(request_csrf_token, csrf_token): return self._reject(request, REASON_BAD_TOKEN) return self._accept(request)
def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(request, "csrf_processing_done", False): return None # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works if getattr(callback, "csrf_exempt", False): 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.is_secure(): # 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. referer = request.META.get("HTTP_REFERER") # -- Change from original here -- # # Only checks referer if it is present. # It's not a failure condition if the referer is not present. # Our site is HTTPS-only which does not need to rely on # referer checking. Above example does not apply. if referer is not None: referer = urlparse(referer) # Make sure we have a valid URL for Referer. if "" in (referer.scheme, referer.netloc): return self._reject(request, REASON_MALFORMED_REFERER) # Ensure that our Referer is also secure. if referer.scheme != "https" and not referer.netloc.endswith( ".onion"): return self._reject(request, REASON_INSECURE_REFERER) # If there isn't a CSRF_COOKIE_DOMAIN, require an exact match # match on host:port. If not, obey the cookie rules (or those # for the session cookie, if CSRF_USE_SESSIONS). good_referer = (settings.SESSION_COOKIE_DOMAIN if settings.CSRF_USE_SESSIONS else settings.CSRF_COOKIE_DOMAIN) if good_referer is not None: server_port = request.get_port() if server_port not in ("443", "80"): good_referer = "%s:%s" % (good_referer, server_port) else: try: # request.get_host() includes the port. good_referer = request.get_host() except DisallowedHost: pass # Create a list of all acceptable HTTP referers, including the # current host if it's permitted by ALLOWED_HOSTS. good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) if good_referer is not None: good_hosts.append(good_referer) if not any( is_same_domain(referer.netloc, host) for host in good_hosts): reason = REASON_BAD_REFERER % referer.geturl() return self._reject(request, reason) 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 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) return self._accept(request)
from __future__ import unicode_literals
def referring_host_is_allowed(host): for pattern in settings.ALLOWED_HOSTS: if is_same_domain(host, pattern): return True return False
def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(request, 'csrf_processing_done', False): return None # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works if getattr(callback, 'csrf_exempt', False): 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) # 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. referer = request.META.get('HTTP_REFERER') if referer is None: return self._reject(request, REASON_NO_REFERER) try: referer = urlparse(referer) except ValueError: return self._reject(request, REASON_MALFORMED_REFERER) # Make sure we have a valid URL for Referer. if '' in (referer.scheme, referer.netloc): return self._reject(request, REASON_MALFORMED_REFERER) # Ensure that our Referer is also secure. if referer.scheme != 'https': return self._reject(request, REASON_INSECURE_REFERER) good_referer = (settings.SESSION_COOKIE_DOMAIN if settings.CSRF_USE_SESSIONS else settings.CSRF_COOKIE_DOMAIN) if good_referer is None: # If no cookie domain is configured, allow matching the # current host:port exactly if it's permitted by # ALLOWED_HOSTS. try: # request.get_host() includes the port. good_referer = request.get_host() except DisallowedHost: pass else: server_port = request.get_port() if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) # Create an iterable of all acceptable HTTP referers. good_hosts = self.csrf_trusted_origins_hosts if good_referer is not None: good_hosts = (*good_hosts, good_referer) if not any( is_same_domain(referer.netloc, host) for host in good_hosts): reason = REASON_BAD_REFERER % referer.geturl() return self._reject(request, 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 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) return self._accept(request)
def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(request, 'csrf_processing_done', False): return None try: cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME] except KeyError: csrf_token = None else: csrf_token = _sanitize_token(cookie_token) if csrf_token != cookie_token: # Cookie token needed to be replaced; # the cookie needs to be reset. request.csrf_cookie_needs_reset = True # Use same token next time. request.META['CSRF_COOKIE'] = csrf_token # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works if getattr(callback, 'csrf_exempt', False): 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.is_secure(): # 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. referer = force_text(request.META.get('HTTP_REFERER'), strings_only=True, errors='replace') if referer is not None: referer = urlparse(referer) # Make sure we have a valid URL for Referer. if '' in (referer.scheme, referer.netloc): return self._reject(request, REASON_MALFORMED_REFERER) # Ensure that our Referer is also secure. if referer.scheme != 'https': return self._reject(request, REASON_INSECURE_REFERER) # If there isn't a CSRF_COOKIE_DOMAIN, assume we need an exact # match on host:port. If not, obey the cookie rules. if settings.CSRF_COOKIE_DOMAIN is None: # request.get_host() includes the port. good_referer = request.get_host() else: good_referer = settings.CSRF_COOKIE_DOMAIN server_port = request.get_port() if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) # Here we generate a list of all acceptable HTTP referers, # including the current host since that has been validated # upstream. good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) good_hosts.append(good_referer) if not any( is_same_domain(referer.netloc, host) for host in good_hosts): reason = REASON_BAD_REFERER % referer.geturl() return self._reject(request, reason) 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 IOError: # 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, '') request_csrf_token = _sanitize_token(request_csrf_token) if not _compare_salted_tokens(request_csrf_token, csrf_token): return self._reject(request, REASON_BAD_TOKEN) return self._accept(request)
) if good_referer is not None: server_port = request.get_port() if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) else: # request.get_host() includes the port. good_referer = request.get_host() # Here we generate a list of all acceptable HTTP referers, # including the current host since that has been validated # upstream. good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) good_hosts.append(good_referer) if not any(is_same_domain(referer.netloc, host) for host in good_hosts): reason = REASON_BAD_REFERER % referer.geturl() return self._reject(request, reason) csrf_token = request.META.get('CSRF_COOKIE') 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', '')