csrf middleware
Source brief analysis:
def process_request(self, request):
# 从cookies中获取csrf_token
csrf_token = self._get_token(request)
if csrf_token is not None:
# Use same token next time.
# 将csrf_token添加到request的头部信息中
request.META['CSRF_COOKIE'] = csrf_token
def process_view(self, request, callback, callback_args, callback_kwargs):
# csrf校验已经完成
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'):
# 如果不需要进行csrf校验
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)
# 如果请求方式是https
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 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, 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:
# 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)
# 从MATA中获取'CSRF_COOKIE'(在process_request中设置的)
csrf_token = request.META.get('CSRF_COOKIE')
# 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.
return self._reject(request, REASON_NO_CSRF_COOKIE)
# Check non-cookie token for match.
request_csrf_token = ""
if request.method == "POST":
try:
# 在POST提交的数据中获取'csrfmiddlewaretoken'
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
# 如果POST数据中没有,则尝试从MATA中获取'x-csrftoken'
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)
# 如果request_csrf_token和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_response(self, request, response):
# 如果不需要需要重新设置csrftoken
if not getattr(request, 'csrf_cookie_needs_reset', False):
if getattr(response, 'csrf_cookie_set', False):
return response
if not request.META.get("CSRF_COOKIE_USED", False):
return response
# Set the CSRF cookie even if it's already set, so we renew
# the expiry timer.
# 依据META中的'CSRF_COOKIE'的值来设置csrftoken
self._set_token(request, response)
response.csrf_cookie_set = True
return response
csrf related decorators:
from django.views.decorators.csrf import csrf_exempt, csrf_protect
# csrf_exempt 豁免csrf校验
# csrf_protect 强制进行csrf校验
from django.utils.decorators import method_decorator
@method_decorator(csrf_exempt, name='dispatch')
# csrf_exempt要加在CBV上,只能加dispatch上
csrf check:
. / 1 wants to pass a prerequisite csrf check: There must be csrftoken
acookie
{% csrf_token %}
Adding the POST request datacsrfmiddlewaretoken
from django.views.decorators.csrf import ensure_csrf_cookie
/ 2 from the cookie
acquired csrftoken
data submitted POST values in csrfmiddlewaretoken
the comparison value made (if not obtain from request.POST in csrfmiddlewaretoken
value, attempts to get the request from the head of the META x-csrftoken
value) to take this value and do csrftoken In contrast, comparison of success by check.