You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 08:25:32