Django的CsrfViewMiddleware通过比对Cookie中的csrftoken与POST数据或X-CSRFToken头的值来校验CSRF,仅对非安全方法强制校验,需置于SessionMiddleware之后,且依赖正确配置的Cookie策略与前后端token传递。

CSRF中间件怎么起作用的
Django的django.middleware.csrf.CsrfViewMiddleware在请求进来时读取csrftoken Cookie,再从POST数据或X-CSRFToken请求头里找匹配值;响应返回前会刷新Cookie(如果需要)。它默认对所有非安全方法(POST/PUT/PATCH/DELETE)强制校验,但跳过GET/HEAD/OPTIONS/TRACE。
常见错误现象:403 Forbidden + Forbidden (CSRF token missing or incorrect.),通常是因为没传token、Cookie被浏览器拦截(比如跨域未设Samesite=None; Secure),或者中间件顺序错了——它必须在SessionMiddleware之后,否则拿不到session里的token种子。
实操建议:
-
CsrfViewMiddleware不能禁用,除非你明确用@csrf_exempt标记个别视图 - 若用AJAX发POST,别只依赖模板里的
{% csrf_token %},得手动把csrftokenCookie值塞进X-CSRFToken请求头 - 前后端分离时,后端需确保
CSRF_COOKIE_SECURE和CSRF_COOKIE_SAMESITE配置与前端域名、协议一致
模板里{% csrf_token %}到底干了什么
这个标签不是“加个隐藏字段”那么简单:它先检查当前请求是否已生成CSRF token(比如通过session或签名cookie),没有就生成一个,然后输出形如<input type="hidden" name="csrfmiddlewaretoken" value="...">的HTML。值来自get_token(request),本质是加密后的随机字符串+时间戳+session key(如果启用了session)。
使用场景:仅适用于表单提交(<form method="post">),对AJAX无直接帮助;如果你用render()返回模板,它自动生效;但用JsonResponse或纯API视图,这个标签压根不渲染。
实操建议:
- 不要在模板里重复写两次
{% csrf_token %},Django不会报错但会多一个冗余字段 - 如果表单action指向外部域名(比如支付回调页),删掉它——CSRF保护只对同源提交有效
- 自定义表单类时,别误删
csrf_token上下文变量,否则{% csrf_token %}输出为空
为什么AJAX POST老是403,但表单却正常
根本原因:表单提交靠浏览器自动带上Cookie,而现代fetch或axios默认不带凭证(credentials: 'same-origin'没开),导致CsrfViewMiddleware读不到csrftoken Cookie,只能退而求其次查X-CSRFToken头——但前端没设。
性能影响:每次AJAX请求都得额外读一次Cookie再解析,开销极小;但若频繁跨域且没配好Samesite,token可能根本送不到后端。
实操建议:
- 前端JS里用
document.cookie.match(/(?:^|; )csrftoken=([^;]*)/)取值,再塞进headers: {'X-CSRFToken': token} - 避免用
localStorage存token——它不随请求自动发送,还得手动补头,且有XSS泄露风险 - 如果用Django REST Framework,
SessionAuthentication会自动处理CSRF,但TokenAuthentication不会,得自己加逻辑
哪些情况可以跳过CSRF校验
CSRF防护只针对状态变更请求,且前提是攻击者能诱导用户发起请求。静态资源、只读API、Webhook接收端、内部服务调用,都不需要它。
容易踩的坑:@csrf_exempt是全局关开关,哪怕只对某个path开放,也会让整个视图函数裸奔;更安全的做法是用@csrf_protect显式包住需要校验的分支,其余放行。
实操建议:
- Webhook接口(比如GitHub、Stripe回调)必须
@csrf_exempt,因为调用方不可能带你的CSRF token - 用
django.views.decorators.csrf.ensure_csrf_cookie只为发Cookie,不校验请求,适合单页应用首次加载 - 测试环境慎用
CSRF_COOKIE_HTTPONLY = False,否则JS取不到token,但生产环境必须为True防XSS窃取










