使用csrf-csrf替代csurf时出现invalid csrf token错误求助
使用csrf-csrf替代csurf时遇到的CSRF令牌验证失败问题
令牌已成功生成,但提交表单时出现“invalid csrf token”错误。
现有代码
app.js:
app.use((req, res, next) => { res.locals.isAuthenticated = req.session.isLoggedIn; res.locals.csrfToken = generateToken(res); console.log(res.locals.csrfToken); next(); });
视图模板:
<form class="login-form" action="/signup" method="POST"> ... <input type="hidden" name="_csrf" value="<%= csrfToken %>" > <button class="btn" type="submit">Signup</button> </form>
错误信息
ForbiddenError: invalid csrf token at doubleCsrf (/workspace/nodejs-practice/node_modules/csrf-csrf/lib/cjs/index.cjs:18:61) at Object.<anonymous> (/workspace/nodejs-practice/app.js:24:5) at Module._compile (node:internal/modules/cjs/loader:1165:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1219:10) at Module.load (node:internal/modules/cjs/loader:1043:32) at Function.Module._load (node:internal/modules/cjs/loader:878:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) at node:internal/main/run_main_module:22:47
排查发现
查看csrf-csrf源码后,发现它默认从请求头获取令牌:
getTokenFromRequest = (req) => req.headers["x-csrf-token"]
当前场景中该请求头值为undefined,需要解决如何将令牌添加到请求头中的问题。
解决方案
方案1:修改csrf-csrf的令牌获取逻辑,兼容表单提交的_csrf字段
csrf-csrf支持自定义令牌获取函数,初始化中间件时,让它同时支持从请求体的_csrf字段和请求头获取令牌,传统表单提交即可正常验证。
修改app.js中csrf中间件的初始化代码:
const { doubleCsrf } = require("csrf-csrf"); const { validateRequest, generateToken } = doubleCsrf({ getTokenFromRequest: (req) => { // 优先从请求头取,无值则从请求体的_csrf字段取 return req.headers["x-csrf-token"] || req.body._csrf; }, // 保留原有secret、cookieName等配置 }); // 注册路由时使用验证中间件 app.post("/signup", validateRequest, (req, res) => { // 你的注册业务逻辑 });
注意:需确保Express已配置请求体解析中间件(如
express.urlencoded({ extended: true })),否则req.body._csrf无法获取值。
方案2:用JavaScript拦截表单提交,将令牌添加到请求头
若不想修改中间件配置,可通过前端脚本拦截表单提交事件,把隐藏字段中的令牌放入请求头:
在视图模板的表单后添加脚本:
<form class="login-form" action="/signup" method="POST" id="signupForm"> ... <input type="hidden" name="_csrf" value="<%= csrfToken %>" id="csrfToken"> <button class="btn" type="submit">Signup</button> </form> <script> document.getElementById('signupForm').addEventListener('submit', function(e) { e.preventDefault(); // 阻止默认同步提交 const csrfToken = document.getElementById('csrfToken').value; const formData = new FormData(this); // 用fetch发送带CSRF头的POST请求 fetch(this.action, { method: this.method, headers: { 'X-CSRF-Token': csrfToken }, body: formData }) .then(response => { if (response.ok) { window.location.href = '/dashboard'; // 验证成功后跳转 } else { console.error('提交失败'); } }) .catch(err => console.error(err)); }); </script>
通用注意事项
- 确认
generateToken已将令牌正确写入响应Cookie(csrf-csrf默认会完成此操作,验证时会对比Cookie与请求中的令牌)
内容的提问来源于stack exchange,提问作者Howard Ding
相关产品推荐
相关产品推荐

