Django Documentation
csrf保护基于以下:
1. 一个CSRF cookie 基于一个随机生成的值,其他网站无法得到。此cookie由CsrfViewMiddleware
产生。它与每个调用django.middleware.csrf.get_token()
(这是一个用于取回CSRF token的方法)的响应一起发送,如果它尚未在请求上设置的话。
为了防止BREACH攻击,token
不仅仅是秘密;随机的salt
被置于secret
之前并用来加密它。出于安全原因,每次用户登录时都会更改密钥的值。
所有传出POST表单中都有一个名为
csrfmiddlewaretoken
的隐藏表单字段。此字段的值同样是秘密的值。salt添加到它并用于加扰它。每次调用get_token()时都会重新生成salt,以便在每个此类响应中更改表单字段值。这部分由template的{% csrf_token %}
完成。对于未使用
HTTP
GET
,HEAD
,OPTIONS
或TRACE
的所有传入请求,必须带有CSRF cookie
,并且csrfmiddlewaretoken
字段必须存在且正确。如果不是,用户将收到403错误。
验证csrfmiddlewaretoken
字段值时,只将secret而不是整个token与cookie值中的secret
进行比较。这允许使用不断变化的token
。虽然每个请求都可以使用自己的token
,但secret
仍然是所有人共同的。
此检查由CsrfViewMiddleware
完成。此外,对于
HTTPS
请求,严格的引用检查由CsrfViewMiddleware
完成。这意味着即使子域可以在您的域上设置或修改cookie
,它也不能强制用户发布到您的应用程序,因为该请求不会来自您自己的确切域。 这也解决了在使用会话独立秘密时在HTTPS下可能发生的中间人攻击,因为即使在HTTPS下与站点通信时,HTTP Set-Cookie标头(不幸)也被客户接受了。 。 (对HTTP请求不进行引用检查,因为在HTTP下,Referer头的存在不够可靠。) 如果设置了CSRF_COOKIE_DOMAIN
设置,则会将引用者与其进行比较。此设置支持子域。例如,CSRF_COOKIE_DOMAIN ='.example.com'
将允许来自www.example.com
和api.example.com
的POST请求。如果未设置该设置,则referer
必须与HTTP Host标头匹配。 可以使用CSRF_TRUSTED_ORIGINS
设置将已接受的引用扩展到当前主机或cookie域之外。
流程图
CsrfViewMiddleware.process_request
# django/middleware/csrf.py
class CsrfViewMiddleware(MiddlewareMixin):
def process_request(self, request):
csrf_token = self._get_token(request)
# 第一次访问,csrf_token返回None,
if csrf_token is not None:
# Use same token next time.
request.META['CSRF_COOKIE'] = csrf_token
# request.META 是一个 Python 字典,包含了所有本次 HTTP 请求的 Header
# 信息,比如用户 IP 地址和用户Agent(通常是浏览器的名称和版本号)。
settings = LazySettings()
这是一个惰性加载, 参考
- Django 源码阅读(二): settings懒加载
- django/conf/global_settings.py
方法_get_token
,从名字上来看就是获取token,_get_token
在后面多处地方都有用到
# django/middleware/csrf.py
def _get_token(self, request):
# CSRF_USE_SESSIONS在django/conf/global_settings.py,默认为False,执行else
if settings.CSRF_USE_SESSIONS:
try:
return 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%s.' % ('_CLASSES' if settings.MIDDLEWARE is None else '')
)
else:
try:
cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]
# CSRF_SESSION_KEY= "csrftoken"
except KeyError:
# 第一次访问的时候 request.COOKIES = {},所以直接返回
return None
csrf_token = _sanitize_token(cookie_token)
# csrf 对不上 cookie里 的 token,标记csrf_cookie_needs_reset=True,
# 在process_response的方法中判定
if csrf_token != cookie_token:
# Cookie token needed to be replaced;
# the cookie needs to be reset.
request.csrf_cookie_needs_reset = True
return csrf_token
# /django/middleware/csrf.py
CSRF_SECRET_LENGTH = 32
CSRF_TOKEN_LENGTH = 2 * CSRF_SECRET_LENGTH