Python 24 Django之csrf中间件
CSRF:跨站请求伪造
一、两种方式通过csrf校验
1、在模板中写上{% csrf_token %}
2、csrf_exempt,csrf_protect装饰器
from django.views.decorators.csrf import csrf_exempt, csrf_protect @csrf_exempt # 如果使用csrf中间件,加上这个装饰器可以直接通过检验 def login(request): return render(xxx) @csrf_protect # 如果不使用csrf中间件,加上这个装饰器可以单独进行检验 def login(request): return render(xx)
二、csrf中间件源码解析
1、文字版流程
process_request:
从请求的cookie中获取csrftoken的值,赋给csrf_token变量,再request.META["CSRF_COOKIE"] = csrf_token
process_view:
① 如果视图加上了csrf_exempt装饰器,不做校验
② 如果请求方式是GET / HEAD / OPTION / TRACE中的一种,不做校验
③ 其他情况做校验:
csrf_token = request.META["CSRF_COOKIE"],
request_csrf_token = ""
# 首先从form表单中获取csrfmiddlewaretoken的值
request_csrf_token = request.POST.get("csrfmiddlewaretoken", "")
# 如果表单中没获取到,则取请求头中获取X-csrftoken的值
request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, "")
将request_csrf_token和csrf_token做比对校验,如果校验成功则继续,不成功则拒绝。
2、源码流程
class CsrfViewMiddleware(MiddlewareMixin): def _get_token(self, request): if settings.CSRF_USE_SESSIONS: #默认是False,忽略 # 省略 pass else: try: cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME] #从浏览器中获取cookie中csrf_token的值 except KeyError: return None 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 # 如果清洗前后的值不一致,则需要重置cookie return csrf_token def process_request(self, request): csrf_token = self._get_token(request) if csrf_token is not None: # Use same token next time. request.META['CSRF_COOKIE'] = csrf_token def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(request, 'csrf_processing_done', False): return None if getattr(callback, 'csrf_exempt', False): # 如果视图函数设置了csrf_exempt装饰器,则不进行校验 return None if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): # 如果是这些方法,则不进行校验 csrf_token = request.META.get('CSRF_COOKIE') # 否则获取到cookie中csrf_token的值 if csrf_token is None: return self._reject(request, REASON_NO_CSRF_COOKIE) # 如果没有这个值则不通过 request_csrf_token = "" if request.method == "POST": try: request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') # 获取页面表单中的值 except IOError: pass if request_csrf_token == "": 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) # 如果表单中的值和cookie中的值不一致则拒绝 return self._accept(request)