CSRF Token 必须与 Session 绑定,服务端生成后存入 Session 并显式传回前端;不能仅依赖 Cookie 或 SameSite,需配合 token 刷新、来源校验及生命周期同步。
CSRF Token 必须和 Session 绑定,不能只靠 Cookie
很多开发者以为只要设置了 httponly 和 secure 的 cookie 就安全了,其实完全不是。csrf 攻击不依赖读取 cookie,而是利用浏览器自动携带 cookie 的特性发起伪造请求。所以关键不是“藏好 cookie”,而是“验证这次请求真是用户本意”。csrf_token 必须在服务端生成后存入 session(比如 redis 或内存 session),并在表单或 api 请求头中显式传回;不能只靠 cookie 自动带、也不该把 token 直接写进 cookie 里返回给前端。
- 常见错误:前端从
document.cookie读取一个叫csrftoken的 Cookie 发起请求 —— 这个值如果没和服务端 Session 关联,攻击者可提前诱导用户访问恶意页面,触发一次合法请求拿到 token,再复用 - 正确做法:每次新建 Session 时生成唯一
csrf_token,存入 Session 存储,并在渲染 HTML 时注入到<meta name="csrf-token" content="...">或表单 hidden 字段;AJAX 请求统一从那里取值,通过X-CSRF-Token头发送 - 注意:Token 不能重用,建议每次 POST/PUT/DELETE 后服务端主动刷新(或至少校验后失效),否则存在重放风险
Django/Flask/Express 中 CSRF 配置的典型陷阱
框架自带的 CSRF 保护看似开箱即用,但默认行为常被误解。比如 Django 的 csrf_protect 装饰器对 class-based view 生效逻辑不同;Flask-WTF 默认只保护表单提交,不自动拦截 JSON 接口;Express 的 csurf 中间件已废弃,新项目得用 csurf 替代品如 csrf 包,且必须手动注入 token 到模板。
- Django:若用
JsonResponse返回数据但前端用fetch提交 JSON,@csrf_protect不生效 —— 必须确保请求头含X-CSRFToken,且该值来自get_token(request)生成并传给前端 - Flask:启用
WTF_CSRF_ENABLED = True后,所有POST路由都会检查 form 中的csrf_token字段,但如果你用request.get_json(),它不会自动解析 header,需手动调用validate_csrf() - Express:
csrf({ cookie: true })会让中间件往响应写一个_csrfCookie,但这个值**不是**用于校验的 token —— 真正要校验的是req.csrfToken()返回的那个,必须由路由显式返回给前端
SameSite Cookie 属性不是万能解药,尤其对老浏览器
SameSite=Lax 能挡住大部分跨站 POST,但它对 GET 请求放行(比如点击链接跳转),而有些业务逻辑确实绑在 GET 上(如注销接口 /logout?next=/)。更麻烦的是,IE11、旧版 Safari 和部分安卓 WebView 完全不支持 SameSite,设了也白设。
- 不要单独依赖
SameSite防御 CSRF —— 它是辅助手段,不是替代方案 -
SameSite=Strict会破坏正常跨站导航体验(比如从搜索引擎点进来打不开页面),实际很少用 - 如果必须兼容 IE11,得回退到传统双 cookie + token 校验:服务端发两个 Cookie,一个
session_id(HttpOnly),一个csrf_cookie(非HttpOnly),前端读后者拼进请求头,服务端比对两者是否匹配
前后端分离场景下 CSRF Token 怎么安全透出
纯静态前端(Vue/React)部署在 Nginx,后端是独立 API 服务,跨域情况下 Cookie 默认不带,SameSite 更无效。这时候不能靠 Cookie 传 token,必须走显式通道,但又不能暴露在 URL 或 localStorage 里(XSS 可读)。
- 推荐方式:首次加载 HTML 时,后端模板直接注入 token 到全局 JS 变量(如
window.CSRF_TOKEN = "abc123"),前端 axios/fetch 拦截器统一加X-CSRF-Token头 - 避免方式:不要把 token 存
localStorage或sessionStorage—— XSS 一打就全漏;也不要放在 URL 参数里 —— 日志、代理、Referer 都可能泄露 - 注意:如果首页是纯静态 HTML(无服务端模板),就得额外开一个免 CSRF 的
/api/csrf-token接口,用GET返回 token,但该接口必须限制来源(Origin校验)且禁止缓存
真正容易被忽略的是 token 生命周期管理 —— 很多人生成一次就长期有效,或者和 session 过期时间不一致。Session 过期了,token 却还能用;或者用户登出时没清 token,别人捡到就能继续用。这事关权限边界,得和服务端 session 的销毁动作严格同步。

















