Node.js中CSRF令牌使用后未失效的问题求助
问题分析与解决方案
你的问题核心在于当前实现没有对CSRF令牌的单次有效性做校验,同时使用了全局固定的secret,导致只要令牌能通过secret验证,不管是否用过都能提交成功。
代码中的问题点
- 全局共享secret:你初始化了一个全局的
csrfToken(实际是csrf库的secret值),所有请求都用这个secret生成和验证令牌。csrf库的verify方法只校验令牌是否由该secret生成,不会检查令牌是否已被使用过。 - 无令牌失效机制:没有维护已使用令牌的记录,无法判断当前提交的令牌是否是第一次使用。
修复方案
要实现令牌单次使用即失效,需要:
- 为每个用户会话维护独立的secret(而非全局共享)
- 记录每个会话已使用过的令牌,验证时同时检查令牌的有效性和未使用状态
以下是修改后的代码示例(结合express-session实现会话存储):
1. 依赖安装与基础配置
npm install express-session csrf
2. 主应用代码修改
import csrf from 'csrf'; import express from 'express'; import session from 'express-session'; const App = express(); // 配置session,用于存储每个用户的csrf secret和已使用令牌 App.use(session({ secret: 'your-session-secret', resave: false, saveUninitialized: true, cookie: { secure: process.env.NODE_ENV === 'production' } })); const tokens = new csrf({ cookie: true }); App.use(express.urlencoded({ extended: true })); App.use(express.json()); App.use((req, res, next) => { // 为当前会话初始化csrf secret和已使用令牌列表 if (!req.session.csrfSecret) { req.session.csrfSecret = tokens.secretSync({ saltLength: 128 }); req.session.usedCsrfTokens = []; } // 非GET/OPTIONS请求验证csrf令牌 if (!['get', 'options'].includes(req.method.toLowerCase())) { const submittedToken = req.body._csrf || req.headers['x-csrf-token']; // 1. 验证令牌是否有效(由当前会话的secret生成) const isTokenValid = tokens.verify(req.session.csrfSecret, submittedToken); // 2. 验证令牌是否未被使用过 const isTokenUnused = !req.session.usedCsrfTokens.includes(submittedToken); if (!isTokenValid || !isTokenUnused) { return res.status(403).json({ error: 'CSRF: Invalid or reused token' }); } // 标记令牌为已使用 req.session.usedCsrfTokens.push(submittedToken); } // 生成新的csrf令牌供后续请求使用 req.csrfToken = tokens.create(req.session.csrfSecret); next(); }); // 引入controller import { csrfToken } from './controller.js'; App.get('/csrf-token', csrfToken); // 其他路由示例 App.post('/submit', (req, res) => { res.json({ success: true }); }); App.listen(3000);
3. controller.js 无需修改
const csrfToken = async (req, res) => res.json({ csrf: req.csrfToken }); export { csrfToken, };
关键改进点
- 每个用户会话拥有独立的csrf secret,避免全局共享带来的安全风险
- 新增
usedCsrfTokens数组记录已使用的令牌,验证时双重校验(有效性+未使用) - 验证通过后立即将令牌加入已使用列表,禁止后续复用
附请求发送截图及应用请求日志截图:

内容的提问来源于stack exchange,提问作者Mr.Singh
相关产品推荐
相关产品推荐

