fastify-session结合postgres存储下的fastify-csrf实现问题咨询
问题1:CSRF token的生成时机
- 既不需要每次请求都生成,也不需要严格限制仅在session cookie首次生成时生成。CSRF token和当前用户会话绑定,同一会话有效期内生成的所有token均合法。你可以根据安全需求选择:
- 方案1:会话生命周期内仅生成一次,开发成本低,适合普通业务场景
- 方案2:每次前端获取token时都重新生成,安全性更高,适合对安全要求高的业务
问题2:generateCsrf示例路由的适用范围
- 不是仅能应用在/login路由上,所有需要给前端返回CSRF token的场景都可以调用该方法。比如单页应用可以单独做一个
/api/get-csrf公共路由用于前端初始化时拉取token,也可以在首页、注册页、登录页等用户首次访问的路由返回token。
注意你当前配置的
saveUninitialized: false会导致未初始化会话的用户无法生成CSRF token,如果你允许未登录用户提交表单/请求(比如注册、游客留言),需要把该配置改为true,或者在对应生成token的路由中手动初始化会话。
问题3:篡改校验的触发时机
- Cookie篡改校验:你配置的
secret参数会让fastify-session和fastify-cookie自动完成所有请求的cookie签名校验,校验失败会自动清除无效cookie,不需要你手动处理。 - CSRF校验:需要你手动给所有非GET/HEAD/OPTIONS请求(即会修改服务端数据的请求)添加校验逻辑,有两种实现方式:
- 全局添加preHandler钩子,排除不需要校验的路由(比如获取token的路由、登录页GET请求)
- 给需要校验的路由单独添加
csrfProtection: true配置
修正后的代码示例
你的原代码存在插件注册顺序错误:fastify-session需要注册在fastify-csrf之前,否则csrf无法读取会话数据,修正后代码如下:
const cookie = require('fastify-cookie'); const session = require('fastify-session'); const csrf = require('fastify-csrf'); const pgSession = require('connect-pg-simple')(session); const fp = require('fastify-plugin'); /** * @param {import('fastify').FastifyInstance} fastify */ const plugin = async (fastify) => { fastify.register(cookie); // 先注册session,再注册csrf fastify.register(session, { store: new pgSession({ conString: process.env.DATABASE_URL, tableName: 'user_session', }), secret: process.env.SESSION_SECRET, saveUninitialized: false, // 若需要支持游客态的CSRF校验请改为true cookie: { httpOnly: true, secure: process.env.NODE_ENV !== 'development', maxAge: 86400 * 1000, sameSite: 'lax' // 建议加上,进一步降低CSRF风险 }, }); fastify.register(csrf, { sessionPlugin: 'fastify-session' }); // 通用获取CSRF token的路由,前端初始化时调用即可 fastify.route({ method: 'GET', path: '/api/get-csrf', handler: async (req, reply) => { const token = await reply.generateCsrf(); return { csrfToken: token }; }, }); // 全局CSRF校验逻辑,自动校验所有非GET类请求 fastify.addHook('preHandler', async (req, reply) => { // 排除不需要校验的请求 if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) return; // 手动触发CSRF校验,校验失败会自动返回403 await req.csrfProtection(); }); fastify.addHook('preHandler', (req, reply, next) => { if (req.session && !req.session.user) req.session.user = {}; next(); }); }; module.exports = fp(plugin);
内容的提问来源于stack exchange,提问作者user616
相关产品推荐
相关产品推荐

