You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

无Cookie/Session的Bearer认证Node.js API配置csurf报错求助

解决无Cookie/Session + Bearer认证API的CSRF配置问题

我来帮你拆解这两个报错的根源,再给出适配你无状态API场景的完整解决方案:

先说说两个错误的原因

第一段代码的 Error: misconfigured csrf

你直接调用了app.use(csrf()),但csurf模块默认要求必须有cookie解析中间件(比如cookie-parser)或者session中间件来存储CSRF秘钥。你既没配置这些中间件,也没指定其他存储方式,模块检测到配置不完整,就抛出了这个错误。

第二段代码的 cannot read session of undefined

你设置了cookie: false,这会让csurf转而尝试从req.session里拿秘钥,但你还是没配置session中间件,req.session根本不存在,自然就报读取错误了。

适配无状态Bearer认证的CSRF解决方案

你的API用Bearer令牌做认证,完全无Cookie/Session,那我们就得绕开csurf默认的session/cookie依赖,改用用户级的自定义秘钥管理来实现CSRF保护。核心逻辑是:先通过Bearer令牌识别用户,再为每个用户绑定独立的CSRF秘钥,用这个秘钥来生成和验证CSRF令牌。

完整实现代码

首先确保你装了所需依赖:

npm install express csurf body-parser jsonwebtoken

jsonwebtoken用来解析Bearer令牌,你可以换成项目里实际用的JWT工具)

然后是代码:

const express = require('express');
const csrf = require('csurf');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const app = express();

// 1. 解析请求体(支持表单和JSON)
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// 2. 自定义Bearer令牌验证中间件
const verifyBearerToken = (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).send('Unauthorized: Missing valid Bearer token');
  }

  const token = authHeader.split(' ')[1];
  try {
    // 替换成你的JWT秘钥和实际验证逻辑
    const decodedUser = jwt.verify(token, 'YOUR_JWT_SECRET');
    req.user = decodedUser; // 将用户信息挂载到请求对象上
    next();
  } catch (err) {
    return res.status(401).send('Unauthorized: Invalid token');
  }
};

// 3. 模拟用户CSRF秘钥存储(生产环境用Redis/数据库)
const userCsrfSecrets = new Map();

// 4. 配置csurf,使用自定义秘钥逻辑
const csrfProtection = csrf({
  // 从请求头或请求体读取CSRF令牌
  value: (req) => req.body._csrf || req.headers['x-csrf-token'],
  // 禁用cookie存储秘钥
  cookie: false,
  // 为每个用户生成/获取专属CSRF秘钥
  getSecret: (req, done) => {
    const userId = req.user.id;
    if (!userCsrfSecrets.has(userId)) {
      // 生成随机秘钥
      const newSecret = crypto.randomBytes(32).toString('hex');
      userCsrfSecrets.set(userId, newSecret);
    }
    done(null, userCsrfSecrets.get(userId));
  }
});

// 5. 路由配置:先验证Bearer令牌,再启用CSRF保护
app.use(verifyBearerToken);
app.use(csrfProtection);

// 前端调用这个接口获取CSRF令牌
app.get('/get-csrf-token', (req, res) => {
  res.json({ csrfToken: req.csrfToken() });
});

// 需要CSRF验证的POST接口
app.post('/process-data', (req, res) => {
  res.send('Data processed safely!');
});

// 启动服务
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}`);
});

关键细节说明

  • Bearer令牌优先:必须先验证Bearer令牌识别用户,这样才能为每个用户分配独立的CSRF秘钥,避免跨用户的CSRF风险。
  • 秘钥存储:示例里用内存Map模拟,生产环境一定要用Redis或数据库持久化存储,不然重启服务后秘钥会丢失,导致之前的CSRF令牌失效。
  • CSRF令牌传递:前端需要先调用/get-csrf-token拿到令牌,然后在发送POST/PUT/DELETE请求时,把令牌放在请求头X-CSRF-Token或者请求体_csrf里。
  • 场景适配:如果你的API只被非浏览器客户端调用(比如移动端、后端服务),其实可以不用CSRF保护——CSRF攻击本质是利用浏览器的同源策略漏洞,非浏览器客户端不会自动携带Cookie,所以不存在这个风险。

内容的提问来源于stack exchange,提问作者Saran

火山引擎 最新活动