summaryrefslogtreecommitdiff
path: root/django/middleware/csrf.py
diff options
context:
space:
mode:
Diffstat (limited to 'django/middleware/csrf.py')
-rw-r--r--django/middleware/csrf.py137
1 files changed, 74 insertions, 63 deletions
diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py
index 6be68ebd76..94f580fa71 100644
--- a/django/middleware/csrf.py
+++ b/django/middleware/csrf.py
@@ -22,27 +22,29 @@ from django.utils.http import is_same_domain
from django.utils.log import log_response
from django.utils.regex_helper import _lazy_re_compile
-logger = logging.getLogger('django.security.csrf')
+logger = logging.getLogger("django.security.csrf")
# This matches if any character is not in CSRF_ALLOWED_CHARS.
-invalid_token_chars_re = _lazy_re_compile('[^a-zA-Z0-9]')
+invalid_token_chars_re = _lazy_re_compile("[^a-zA-Z0-9]")
REASON_BAD_ORIGIN = "Origin checking failed - %s does not match any trusted origins."
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_MISSING = 'CSRF token missing.'
+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."
+REASON_INSECURE_REFERER = (
+ "Referer checking failed - Referer is insecure while host is secure."
+)
# The reason strings below are for passing to InvalidTokenFormat. They are
# phrases without a subject because they can be in reference to either the CSRF
# cookie or non-cookie token.
-REASON_INCORRECT_LENGTH = 'has incorrect length'
-REASON_INVALID_CHARACTERS = 'has invalid characters'
+REASON_INCORRECT_LENGTH = "has incorrect length"
+REASON_INVALID_CHARACTERS = "has invalid characters"
CSRF_SECRET_LENGTH = 32
CSRF_TOKEN_LENGTH = 2 * CSRF_SECRET_LENGTH
CSRF_ALLOWED_CHARS = string.ascii_letters + string.digits
-CSRF_SESSION_KEY = '_csrftoken'
+CSRF_SESSION_KEY = "_csrftoken"
def _get_failure_view():
@@ -62,7 +64,7 @@ def _mask_cipher_secret(secret):
mask = _get_new_csrf_string()
chars = CSRF_ALLOWED_CHARS
pairs = zip((chars.index(x) for x in secret), (chars.index(x) for x in mask))
- cipher = ''.join(chars[(x + y) % len(chars)] for x, y in pairs)
+ cipher = "".join(chars[(x + y) % len(chars)] for x, y in pairs)
return mask + cipher
@@ -76,21 +78,24 @@ def _unmask_cipher_token(token):
token = token[CSRF_SECRET_LENGTH:]
chars = CSRF_ALLOWED_CHARS
pairs = zip((chars.index(x) for x in token), (chars.index(x) for x in mask))
- return ''.join(chars[x - y] for x, y in pairs) # Note negative values are ok
+ return "".join(chars[x - y] for x, y in pairs) # Note negative values are ok
def _add_new_csrf_cookie(request):
"""Generate a new random CSRF_COOKIE value, and add it to request.META."""
csrf_secret = _get_new_csrf_string()
- request.META.update({
- # RemovedInDjango50Warning: when the deprecation ends, replace
- # with: 'CSRF_COOKIE': csrf_secret
- 'CSRF_COOKIE': (
- _mask_cipher_secret(csrf_secret)
- if settings.CSRF_COOKIE_MASKED else csrf_secret
- ),
- 'CSRF_COOKIE_NEEDS_UPDATE': True,
- })
+ request.META.update(
+ {
+ # RemovedInDjango50Warning: when the deprecation ends, replace
+ # with: 'CSRF_COOKIE': csrf_secret
+ "CSRF_COOKIE": (
+ _mask_cipher_secret(csrf_secret)
+ if settings.CSRF_COOKIE_MASKED
+ else csrf_secret
+ ),
+ "CSRF_COOKIE_NEEDS_UPDATE": True,
+ }
+ )
return csrf_secret
@@ -104,12 +109,12 @@ def get_token(request):
header to the outgoing response. For this reason, you may need to use this
function lazily, as is done by the csrf context processor.
"""
- if 'CSRF_COOKIE' in request.META:
- csrf_secret = request.META['CSRF_COOKIE']
+ if "CSRF_COOKIE" in request.META:
+ csrf_secret = request.META["CSRF_COOKIE"]
# Since the cookie is being used, flag to send the cookie in
# process_response() (even if the client already has it) in order to
# renew the expiry timer.
- request.META['CSRF_COOKIE_NEEDS_UPDATE'] = True
+ request.META["CSRF_COOKIE_NEEDS_UPDATE"] = True
else:
csrf_secret = _add_new_csrf_cookie(request)
return _mask_cipher_secret(csrf_secret)
@@ -171,19 +176,17 @@ class CsrfViewMiddleware(MiddlewareMixin):
This middleware should be used in conjunction with the {% csrf_token %}
template tag.
"""
+
@cached_property
def csrf_trusted_origins_hosts(self):
return [
- urlparse(origin).netloc.lstrip('*')
+ urlparse(origin).netloc.lstrip("*")
for origin in settings.CSRF_TRUSTED_ORIGINS
]
@cached_property
def allowed_origins_exact(self):
- return {
- origin for origin in settings.CSRF_TRUSTED_ORIGINS
- if '*' not in origin
- }
+ return {origin for origin in settings.CSRF_TRUSTED_ORIGINS if "*" not in origin}
@cached_property
def allowed_origin_subdomains(self):
@@ -192,8 +195,12 @@ class CsrfViewMiddleware(MiddlewareMixin):
subdomains of the netloc are allowed.
"""
allowed_origin_subdomains = defaultdict(list)
- for parsed in (urlparse(origin) for origin in settings.CSRF_TRUSTED_ORIGINS if '*' in origin):
- allowed_origin_subdomains[parsed.scheme].append(parsed.netloc.lstrip('*'))
+ for parsed in (
+ urlparse(origin)
+ for origin in settings.CSRF_TRUSTED_ORIGINS
+ if "*" in origin
+ ):
+ allowed_origin_subdomains[parsed.scheme].append(parsed.netloc.lstrip("*"))
return allowed_origin_subdomains
# The _accept and _reject methods currently only exist for the sake of the
@@ -208,7 +215,9 @@ class CsrfViewMiddleware(MiddlewareMixin):
def _reject(self, request, reason):
response = _get_failure_view()(request, reason=reason)
log_response(
- 'Forbidden (%s): %s', reason, request.path,
+ "Forbidden (%s): %s",
+ reason,
+ request.path,
response=response,
request=request,
logger=logger,
@@ -228,9 +237,9 @@ class CsrfViewMiddleware(MiddlewareMixin):
csrf_secret = request.session.get(CSRF_SESSION_KEY)
except AttributeError:
raise ImproperlyConfigured(
- 'CSRF_USE_SESSIONS is enabled, but request.session is not '
- 'set. SessionMiddleware must appear before CsrfViewMiddleware '
- 'in MIDDLEWARE.'
+ "CSRF_USE_SESSIONS is enabled, but request.session is not "
+ "set. SessionMiddleware must appear before CsrfViewMiddleware "
+ "in MIDDLEWARE."
)
else:
try:
@@ -249,12 +258,12 @@ class CsrfViewMiddleware(MiddlewareMixin):
def _set_csrf_cookie(self, request, response):
if settings.CSRF_USE_SESSIONS:
- if request.session.get(CSRF_SESSION_KEY) != request.META['CSRF_COOKIE']:
- request.session[CSRF_SESSION_KEY] = request.META['CSRF_COOKIE']
+ if request.session.get(CSRF_SESSION_KEY) != request.META["CSRF_COOKIE"]:
+ request.session[CSRF_SESSION_KEY] = request.META["CSRF_COOKIE"]
else:
response.set_cookie(
settings.CSRF_COOKIE_NAME,
- request.META['CSRF_COOKIE'],
+ request.META["CSRF_COOKIE"],
max_age=settings.CSRF_COOKIE_AGE,
domain=settings.CSRF_COOKIE_DOMAIN,
path=settings.CSRF_COOKIE_PATH,
@@ -263,17 +272,17 @@ class CsrfViewMiddleware(MiddlewareMixin):
samesite=settings.CSRF_COOKIE_SAMESITE,
)
# Set the Vary header since content varies with the CSRF cookie.
- patch_vary_headers(response, ('Cookie',))
+ patch_vary_headers(response, ("Cookie",))
def _origin_verified(self, request):
- request_origin = request.META['HTTP_ORIGIN']
+ 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_origin = "%s://%s" % (
+ "https" if request.is_secure() else "http",
good_host,
)
if request_origin == good_origin:
@@ -292,7 +301,7 @@ class CsrfViewMiddleware(MiddlewareMixin):
)
def _check_referer(self, request):
- referer = request.META.get('HTTP_REFERER')
+ referer = request.META.get("HTTP_REFERER")
if referer is None:
raise RejectRequest(REASON_NO_REFERER)
@@ -302,11 +311,11 @@ class CsrfViewMiddleware(MiddlewareMixin):
raise RejectRequest(REASON_MALFORMED_REFERER)
# Make sure we have a valid URL for Referer.
- if '' in (referer.scheme, referer.netloc):
+ if "" in (referer.scheme, referer.netloc):
raise RejectRequest(REASON_MALFORMED_REFERER)
# Ensure that our Referer is also secure.
- if referer.scheme != 'https':
+ if referer.scheme != "https":
raise RejectRequest(REASON_INSECURE_REFERER)
if any(
@@ -330,18 +339,18 @@ class CsrfViewMiddleware(MiddlewareMixin):
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 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 _bad_token_message(self, reason, token_source):
- if token_source != 'POST':
+ 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}.'
+ token_source = f"the {header_name!r} HTTP header"
+ return f"CSRF token from {token_source} {reason}."
def _check_token(self, request):
# Access csrf_secret via self._get_secret() as rotate_token() may have
@@ -350,7 +359,7 @@ class CsrfViewMiddleware(MiddlewareMixin):
try:
csrf_secret = self._get_secret(request)
except InvalidTokenFormat as exc:
- raise RejectRequest(f'CSRF cookie {exc.reason}.')
+ raise RejectRequest(f"CSRF cookie {exc.reason}.")
if csrf_secret is None:
# No CSRF cookie. For POST requests, we insist on a CSRF cookie,
@@ -359,10 +368,10 @@ class CsrfViewMiddleware(MiddlewareMixin):
raise RejectRequest(REASON_NO_CSRF_COOKIE)
# Check non-cookie token for match.
- request_csrf_token = ''
- if request.method == 'POST':
+ request_csrf_token = ""
+ if request.method == "POST":
try:
- request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
+ request_csrf_token = request.POST.get("csrfmiddlewaretoken", "")
except UnreadablePostError:
# Handle a broken connection before we've completed reading the
# POST data. process_view shouldn't raise any exceptions, so
@@ -370,7 +379,7 @@ class CsrfViewMiddleware(MiddlewareMixin):
# listening, which they probably aren't because of the error).
pass
- if request_csrf_token == '':
+ if request_csrf_token == "":
# Fall back to X-CSRFToken, to make things easier for AJAX, and
# possible for PUT/DELETE.
try:
@@ -383,7 +392,7 @@ class CsrfViewMiddleware(MiddlewareMixin):
raise RejectRequest(REASON_CSRF_TOKEN_MISSING)
token_source = settings.CSRF_HEADER_NAME
else:
- token_source = 'POST'
+ token_source = "POST"
try:
_check_token_format(request_csrf_token)
@@ -392,7 +401,7 @@ class CsrfViewMiddleware(MiddlewareMixin):
raise RejectRequest(reason)
if not _does_token_match(request_csrf_token, csrf_secret):
- reason = self._bad_token_message('incorrect', token_source)
+ reason = self._bad_token_message("incorrect", token_source)
raise RejectRequest(reason)
def process_request(self, request):
@@ -406,22 +415,22 @@ class CsrfViewMiddleware(MiddlewareMixin):
# masked, this also causes it to be replaced with the unmasked
# form, but only in cases where the secret is already getting
# saved anyways.
- request.META['CSRF_COOKIE'] = csrf_secret
+ request.META["CSRF_COOKIE"] = csrf_secret
def process_view(self, request, callback, callback_args, callback_kwargs):
- if getattr(request, 'csrf_processing_done', False):
+ 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):
+ if getattr(callback, "csrf_exempt", False):
return None
# Assume that anything not defined as 'safe' by RFC7231 needs protection
- if request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
+ if request.method in ("GET", "HEAD", "OPTIONS", "TRACE"):
return self._accept(request)
- if getattr(request, '_dont_enforce_csrf_checks', False):
+ 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
@@ -430,9 +439,11 @@ class CsrfViewMiddleware(MiddlewareMixin):
# Reject the request if the Origin header doesn't match an allowed
# value.
- if 'HTTP_ORIGIN' in request.META:
+ if "HTTP_ORIGIN" in request.META:
if not self._origin_verified(request):
- return self._reject(request, REASON_BAD_ORIGIN % request.META['HTTP_ORIGIN'])
+ 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.
@@ -464,7 +475,7 @@ class CsrfViewMiddleware(MiddlewareMixin):
return self._accept(request)
def process_response(self, request, response):
- if request.META.get('CSRF_COOKIE_NEEDS_UPDATE'):
+ if request.META.get("CSRF_COOKIE_NEEDS_UPDATE"):
self._set_csrf_cookie(request, response)
# Unset the flag to prevent _set_csrf_cookie() from being
# unnecessarily called again in process_response() by other
@@ -473,6 +484,6 @@ class CsrfViewMiddleware(MiddlewareMixin):
# CSRF_COOKIE_NEEDS_UPDATE is still respected in subsequent calls
# e.g. in case rotate_token() is called in process_response() later
# by custom middleware but before those subsequent calls.
- request.META['CSRF_COOKIE_NEEDS_UPDATE'] = False
+ request.META["CSRF_COOKIE_NEEDS_UPDATE"] = False
return response