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的值做对比,对比成功通过校验。
来源:https://www.cnblogs.com/zyyhxbs/p/11493181.html