← 返回题目列表

Django 如何防御 CSRF?还内置了哪些常见安全能力?

高频 中等 第 4 / 27 题 更新于 2026/07/27
DjangoCSRF安全Cookie

简化版

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 可配置 SecureHttpOnlySameSite
  • 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 配置要一起看。