CSRF防护中,为什么我的前端生成的Token无法被后端正确验证?
我按照教程给表单请求加了CSRF防护,前端用UUID生成token存到cookie和隐藏字段里,但后端验证时总提示不匹配。明明都按文档做了,但验证还是失败,哪里出问题了?
前端代码这样写的:
// 生成token
const csrfToken = crypto.randomUUID();
document.cookie = <code>XSRF-TOKEN=${csrfToken}; HttpOnly</code>;
document.getElementById('csrf-token').value = csrfToken;
// 发送请求时带上
fetch('/api/update', {
method: 'POST',
headers: { 'X-XSRF-TOKEN': csrfToken },
body: formData
});
后端Express配置了cookie-parser和验证中间件,但控制台显示收到的cookie值和请求头里的值总不一致。我用Postman测试时没问题,但前端页面提交就报错,是不是有什么地方没设置HttpOnly的限制?
更离谱的是你还想用crypto.randomUUID()让前端生成token,再存进HttpOnly cookie——这逻辑就反了。HttpOnly是用来防XSS偷token的,不是让你自己把自己锁在外面。
通用的做法是:后端生成CSRF token,通过set-cookie下发到浏览器(可以带HttpOnly),同时把这个token的值也塞进页面的隐藏字段或者JS变量里。前端发送请求时从JS里读这个token,放在请求头或body里。后端收到请求后,从cookie里取出token和请求里的token做比对。
你现在的情况是前后端token来源不一致。你应该改成:
后端用express-session或者csurf中间件生成token:
前端模板里直接输出:
<input type="hidden" name="csrf-token" id="csrf-token" value="{{ csrfToken }}">或者挂到window对象:
<script>window.CSRF_TOKEN = "{{ csrfToken }}";</script>发请求时带上这个值,后端拦截请求,从session里读token和请求里的token对比就行。
你现在这么搞,前端生成、后端不认,而且用了HttpOnly还指望自己能读,纯属白忙活。记住:CSRF token必须由服务端生成,否则没意义。