Django 如何防御 CSRF?还内置了哪些常见安全能力?
简化版
Django 通过 CsrfViewMiddleware、CSRF Cookie 和表单里的 csrf token 校验来防御 CSRF。它还提供 XSS 自动转义、点击劫持防护、安全 Cookie、HTTPS 相关配置、Host 校验等能力,但前提是开发者正确启用和配置。
详细版
CSRF 是跨站请求伪造:攻击者诱导已登录用户访问恶意页面,借用户浏览器自动携带 Cookie 的特性,向目标站点发起非本人意愿的请求。
Django 防御思路:
- 对不安全方法,如 POST、PUT、DELETE,校验 CSRF token;
- 模板表单中使用
{% csrf_token %}; - 中间件
CsrfViewMiddleware负责校验; - token 与 Cookie/请求数据配合,证明请求来自本站页面;
- 对 AJAX 请求,需要把 token 放到请求头里。
此外,Django 默认模板会自动转义变量,帮助降低 XSS 风险;ALLOWED_HOSTS 可防 Host Header 攻击;安全中间件可设置多种安全响应头。
完整版教学
一、CSRF 攻击为什么成立
假设用户已经登录银行网站,浏览器里有银行站点 Cookie。攻击者诱导用户访问恶意页面,页面里偷偷放一个表单自动提交:
<form action="https://bank.example/transfer/" method="post">
<input name="to" value="attacker">
<input name="amount" value="1000">
</form>
浏览器请求银行站点时,会自动带上银行 Cookie。服务器如果只看 Cookie 判断登录,就可能误以为这是用户主动操作。
CSRF 的关键不是攻击者偷到了 Cookie,而是利用浏览器自动带 Cookie 的行为。
二、Django 的 CSRF token 机制
Django 要求危险请求带上合法 token。普通模板表单写法:
<form method="post">
{% csrf_token %}
...
</form>
渲染后页面里会带一个隐藏字段。请求提交时,CsrfViewMiddleware 会验证请求里的 token 是否可信。
如果是 AJAX 请求,常见做法是从 Cookie 或页面中读取 token,放入请求头:
fetch("/api/update/", {
method: "POST",
headers: {
"X-CSRFToken": csrfToken
},
body: formData
})
三、哪些请求需要 CSRF 防护
一般原则:会改变服务器状态的请求需要防护。
- GET:应该是安全、幂等的读取,不应修改数据;
- POST/PUT/PATCH/DELETE:可能修改数据,需要 CSRF 校验。
如果把“删除文章”做成 GET 请求,即使有 CSRF 也很危险,因为用户点一个图片链接都可能触发删除。正确设计是:GET 只读,修改动作使用合适的非安全方法并做 CSRF 防护。
四、什么时候会用 csrf_exempt
有些接口可能不适合 CSRF,例如第三方回调接口、纯 token 认证 API。但 csrf_exempt 要谨慎使用:
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def webhook(request):
...
如果豁免 CSRF,就必须有替代安全机制,例如签名校验、Bearer Token、IP 白名单、时间戳防重放等。不能因为接口报 403 就粗暴豁免。
五、Django 其他安全能力
Django 不只管 CSRF,还提供很多安全基础设施:
- 模板自动转义,降低 XSS 风险;
ALLOWED_HOSTS校验 Host;SecurityMiddleware支持 HTTPS 跳转、HSTS 等配置;XFrameOptionsMiddleware防点击劫持;- Cookie 可配置
Secure、HttpOnly、SameSite; - ORM 参数化查询降低 SQL 注入风险;
- 密码哈希体系保护用户密码。
这些能力不是“开了 Django 就万无一失”。如果你使用 mark_safe 乱标记 HTML、手写拼接 SQL、关闭安全中间件,仍然可能出漏洞。
六、常见误区
- 以为 CSRF 是 Cookie 被偷。CSRF 通常利用的是浏览器自动携带 Cookie。
- 为了解决 403 直接全站
csrf_exempt。这等于拆安全门。 - GET 请求做删除、转账、状态变更。GET 应保持只读。
- 认为模板自动转义后就没有 XSS。手动输出 HTML、富文本、前端插入仍要谨慎。
- 只在开发环境验证,生产忘记配置
ALLOWED_HOSTS、HTTPS、安全 Cookie。
七、CSRF 和 XSS、CORS 的边界
CSRF、XSS、CORS 经常被放在一起问,但它们解决的问题不同。CSRF 防的是攻击者借用用户已登录的身份发起跨站请求;XSS 防的是攻击者把恶意脚本注入到页面里执行;CORS 是浏览器对跨源读取响应的限制机制,不是专门的 CSRF 防护机制。
很多人会误以为“接口用了 CORS 就不怕 CSRF”,这不严谨。即使浏览器不允许攻击网站读取响应,只要请求能带上 Cookie 并触发状态修改,CSRF 风险仍然可能存在。防 CSRF 的关键是让攻击者无法伪造合法 token,或者让敏感接口不依赖自动携带的 Cookie 身份。
Django 的 CSRF 中间件主要保护基于 Cookie 的登录态。对于纯 Token/JWT 放在 Authorization Header 的 API,攻击者跨站表单通常不能自动带上 Authorization Header,CSRF 风险模型会变化。但如果 JWT 放在 Cookie 里,仍然要考虑 CSRF。
八、工程实践:什么时候不要随便 csrf_exempt
@csrf_exempt 可以跳过 CSRF 校验,但不能因为“接口调不通”就随便加。对于后台表单、管理操作、Cookie 登录态下的 POST/PUT/DELETE 请求,跳过 CSRF 可能直接引入安全漏洞。
如果是第三方回调接口,例如支付回调、消息推送,确实可能无法提供 Django CSRF token。这时可以对该接口豁免 CSRF,但必须使用其他验证机制,比如签名校验、时间戳、随机 nonce、来源 IP 白名单、幂等处理等。也就是说,豁免 CSRF 不等于没有安全校验。
面试时可以补充 Django 其他安全能力:模板默认转义降低 XSS 风险,SecurityMiddleware 可以设置 HSTS、X-Content-Type-Options 等安全 Header,密码哈希和认证系统提供基础账号安全。这些能力组合起来,才构成 Web 安全防线。
九、常见误区与追问
| 安全概念 | 防护目标 | Django 相关机制 |
|---|---|---|
| CSRF | 借用户登录态伪造请求 | CSRF token、CsrfViewMiddleware |
| XSS | 恶意脚本注入页面 | 模板自动转义、谨慎使用 mark_safe |
| CORS | 跨源读取响应限制 | 浏览器策略和服务端 CORS 配置 |
- 误区:CSRF 是 Cookie 被攻击者偷走。 CSRF 的核心是浏览器会自动携带 Cookie,攻击者借这个登录态发起状态修改请求。
- 误区:接口用了 CORS 就不用 CSRF。 CORS 主要限制跨源读取响应,不等于阻止所有跨站提交;Cookie 登录态下仍要考虑 CSRF。
- 误区:遇到 403 就全站 csrf_exempt。 这会绕开关键防护,正确做法是补 token、Header 或为特定回调设计替代验签。
- 追问:为什么 GET 不应该做删除或转账? GET 应保持安全和幂等语义,浏览器预取、缓存或外链访问都可能触发 GET。
- 追问:JWT API 还需要 CSRF 吗? 如果 JWT 放在 Authorization Header,CSRF 风险较低;如果 JWT 放 Cookie 并自动携带,仍要防 CSRF。
- 追问:第三方回调不能带 CSRF token 怎么办? 可以豁免该接口,但必须用签名、时间戳、nonce、IP 白名单和幂等校验补上安全边界。
记忆钩子:CSRF 防“借身份”,XSS 防“脚本注入”,CORS 管“跨源读响应”;三者边界不同,不能互相替代。
十、加强记忆
CSRF 防的是“借用户登录态发起伪造请求”。Django 用 CSRF token 校验危险请求,模板表单要写 {% csrf_token %},AJAX 要带 X-CSRFToken。安全不是一个开关,CSRF、XSS、Host 校验、安全 Cookie、HTTPS 配置要一起看。