CSRF防护中,为什么我的前端生成的Token无法被后端正确验证?

令狐采涵 阅读 29

我按照教程给表单请求加了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的限制?

我来解答 赞 1 收藏
二维码
手机扫码查看
1 条解答
Mr-诗晴
Mr-诗晴 Lv1
你这问题出在HttpOnly的使用上。你前端代码里把token写进cookie时加了HttpOnly,但HttpOnly的意思就是“浏览器禁止JavaScript读取这个cookie”,那你后面用document.cookie去取值根本拿不到,自然发出去的请求头是空或者旧值。

更离谱的是你还想用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:

app.use(session({
secret: 'your-secret',
httpOnly: true,
}));

app.get('/form', (req, res) => {
const csrfToken = req.session.csrfToken || crypto.randomBytes(18).toString('hex');
req.session.csrfToken = csrfToken;
res.render('form', { csrfToken }); // 把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必须由服务端生成,否则没意义。
点赞 4
2026-02-09 03:04