前端如何配合后端做好CSRF防护?状态变更操作总被拦截怎么办?
我们项目里用的是 cookie + CSRF token 的方案,后端要求所有修改数据的请求(比如 POST、PUT)都必须带一个叫 X-CSRF-Token 的 header。但我发现每次提交表单时 token 要么拿不到,要么过期,本地开发经常 403。
我试过在页面加载时从 meta 标签读取 token,也试过每次请求前重新 fetch 获取,但都不稳定。而且有些按钮是动态渲染的,绑定事件时 token 可能已经变了。有没有更可靠的前端实践?
顺便贴一下我们目前用来隐藏 token 的样式,虽然可能和问题关系不大:
.csrf-token {
display: none;
visibility: hidden;
position: absolute;
width: 0;
height: 0;
overflow: hidden;
}
后端在登录成功后,把 CSRF token 写进一个 httpOnly cookie,比如叫
csrf_token,这样前端 JS 根本读不到,但浏览器会自动带上;然后所有 POST/PUT/DELETE 请求,前端在 header 里塞一个X-CSRF-Token,值就从那个 cookie 里读——对,你没看错,用 cookie 存 token,前端用 JS 读 cookie(非 httpOnly),再手动加 header。关键来了:cookie 要设置
SameSite=None; Secure(本地开发可以先用Lax临时救急,但上线必须 None+Secure),不然跨域或 iframe 场景直接丢 token。前端代码长这样:
动态按钮?别在绑定事件时读 token,读一次就锁死了,应该在真正发请求那一刻再读——上面这个
safeFetch就是干这个的,每次调用都实时从 cookie 拿最新值,过期了就 302 跳登录页重登,总比 403 搞死人强。至于你贴的那段隐藏 token 的 CSS,赶紧删了,那玩意儿是 XSS 漏洞温床,token 放 DOM 里等于裸奔。CSRF token 就该待在 cookie 里,前端只负责“用”,别“存”。
最后补一句:本地开发如果用的是
localhost,cookie 的Secure会失效,记得把开发域名改成dev.local之类的,配个 hosts,或者临时关掉 Secure(只限开发环境,上线前一定改回来)。