bigywater.tech
Django · 70

CSRF Token 的生成、校验与轮换

{% csrf_token %} 渲染的那串字符是什么?为什么每次刷新都不一样却都能通过?登录之后为什么旧页面的表单提交会 403?这篇用真实的 CSRF 中间件把这些行为逐个验证。

32 vs 64
cookie 里是 32 位 secret;表单 token 是 64 位,每次请求重新掩码
403
缺 token、错 token、缺 cookie 都被拒;X-CSRFToken 请求头可代替表单字段
登录轮换
login() 会轮换 CSRF token:登录前的旧表单 token 之后提交得到 403

token 的形态

实测 · 本机执行输出
cookie csrftoken 长度 = 32  两次请求的 cookie 相同: True
表单 token 长度 = 64  两次请求的表单 token 相同: False
CSRF_COOKIE_NAME = csrftoken  CSRF_HEADER_NAME = HTTP_X_CSRFTOKEN  CSRF_USE_SESSIONS = False

服务端不需要存储 token:cookie 里的 secret 就是依据。表单里的 token 是「掩码 + 密文」,每次不同,但解出的 secret 一致。

校验

实测 · 本机执行输出
token 不同但都有效: True [200, 200]
没带 token → status 403 | CSRF verification failed. Request aborted.
错误的 token → status 403 | CSRF verification failed. Request aborted.
没有 cookie(新会话)但带了别人的 token → status 403
用请求头 X-CSRFToken 传 token → status 200

403 页面上具体的失败原因文案本文没有逐个测;测试里必须用 Client(enforce_csrf_checks=True),否则测试客户端默认跳过 CSRF。

轮换

实测 · 本机执行输出
GET 不检查 CSRF: 200
POST /login/ → 200 in  登录前后 csrftoken cookie 相同: False  前: G36cMUy2…  后: JuBxE4Ei…
登录前拿到的旧表单 token 还能用吗: 403
用登录后的新 token: 200
POST /logout/ → 200  退出前后 cookie 相同: True

登录后轮换 token 是防止「会话固定」类的攻击。副作用:用户在登录前打开的其它标签页里的表单会失效。

分步演示:CSRF token

CSRF token
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。django.test.Client(enforce_csrf_checks=True);HTTP 而非 HTTPS
    • 没有验证:HTTPS 下的 Origin / Referer 检查与 CSRF_TRUSTED_ORIGINS(第 123 篇)、CSRF_USE_SESSIONS = True 的行为、SameSite 与 CSRF_COOKIE_SECURE、AJAX 从 cookie 取 token、超出 CSRF_COOKIE_AGE 的 cookie 过期
    京ICP备2026067502号-1京公网安备11010502063487号