csrf的中间件
源码简略分析:
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相关的装饰器:
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校验:
/1.想要能通过csrf校验的前提条件: 必须要有 csrftoken
的 cookie
{% csrf_token %}
,在POST请求数据中添加csrfmiddlewaretoken
from django.views.decorators.csrf import ensure_csrf_cookie
/2..从cookie
获取csrftoken
的值 与 POST提交的数据中的csrfmiddlewaretoken
的值做对比(如果从request.POST中获取不到csrfmiddlewaretoken
的值,会尝试从请求头META中获取x-csrftoken
的值)拿这个值与csrftoken的值做对比,对比成功通过校验。