NodeJS带认证场景下文件上传出现csrf token无效问题的解决方案咨询
解决multipart/form-data表单上传时CSRF Token无效的问题
我之前也碰到过一模一样的问题,用multipart/form-data提交文件时总是触发CSRF验证失败,折腾了好一会儿才找到几个靠谱的解决办法,分享给你:
1. 调整中间件顺序,确保multer在CSRF中间件之前执行
很多时候问题出在中间件的执行顺序上。如果你用的是Express+csurf+multer的组合,一定要让multer先处理请求,这样CSRF中间件才能从请求体里读取到_csrf字段。
举个例子,正确的路由配置应该是这样:
const express = require('express'); const multer = require('multer'); const csurf = require('csurf'); const cookieParser = require('cookie-parser'); const app = express(); app.use(cookieParser()); // 配置multer处理文件上传,这里用磁盘存储为例 const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, './uploads/'); }, filename: function (req, file, cb) { cb(null, Date.now() + '-' + file.originalname); } }); const upload = multer({ storage: storage }); // 初始化CSRF中间件 const csrfProtection = csurf({ cookie: true }); // 路由:先让multer处理文件,再做CSRF验证 app.post('/createtest/question', upload.single('file'), csrfProtection, (req, res) => { // 这里可以访问req.file拿到上传的文件,req.body拿到_csrf等字段 res.send('文件上传成功'); });
如果之前你把CSRF中间件放在multer前面,CSRF中间件会先执行,这时候请求体还没被解析,自然找不到_csrf字段,就会报错。
2. 改用请求头传递CSRF Token
如果表单里的_csrf字段总是无法被正确识别,可以试试把Token放在请求头里。这种方式对multipart/form-data请求兼容性更好:
后端配置
先修改CSRF中间件,让它把Token存在Cookie里:
const csrfProtection = csurf({ cookie: { httpOnly: false, // 允许前端JS读取Cookie sameSite: 'strict' } }); // 在渲染表单的路由里,把Token传给模板(其实这一步可以省略,前端直接从Cookie读) app.get('/upload', csrfProtection, (req, res) => { res.render('upload', { csrfToken: req.csrfToken() }); });
前端修改
去掉表单里的隐藏_csrf字段,用JavaScript监听表单提交,通过请求头传递Token:
<form id="fileUploadForm" action="/createtest/question" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <button type="submit">上传文件</button> </form> <script> // 从Cookie中获取CSRF Token的工具函数 function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); } document.getElementById('fileUploadForm').addEventListener('submit', function(e) { e.preventDefault(); // 阻止默认表单提交 const csrfToken = getCookie('XSRF-TOKEN'); // 注意Cookie名称,默认是XSRF-TOKEN const formData = new FormData(this); // 用fetch提交请求 fetch(this.action, { method: 'POST', headers: { 'X-CSRF-Token': csrfToken // 在请求头里带上Token }, body: formData }) .then(response => response.text()) .then(data => { console.log(data); // 处理上传成功后的逻辑 }) .catch(error => { console.error('上传失败:', error); }); }); </script>
这样做的好处是,请求头里的Token不会被multipart的解析逻辑干扰,CSRF中间件能直接读取到。
3. 检查Token的渲染是否正确
有时候问题很简单,就是模板里的<%= csrfToken %>没有正确输出Token。你可以打开浏览器的开发者工具,查看页面源码,确认隐藏字段的value是不是一个有效的字符串,而不是undefined或者空值。如果是空的,那就要检查后端渲染模板时有没有正确传递csrfToken变量。
比如在Express的路由里,必须确保在渲染模板前先执行CSRF中间件:
app.get('/createtest', csrfProtection, (req, res) => { res.render('createtest', { csrfToken: req.csrfToken() }); });
内容的提问来源于stack exchange,提问作者Vishy
相关产品推荐
相关产品推荐

