Nodejs使用csrf校验,如果form设置multipart/form-data就失效,如何解决
Nodejs使用csrf校验,如果form设置multipart/form-data就失效,如何解决
我使用csrf做校验,但是当我在表单中设置了form的 enctype="multipart/form-data"后,校验一直提示session失效如何解决? 我试着在post时显示用buyboy校验,但是还是一样提示session失效。求解决办法: app.js
app.use csrf()
exports.upload = (req,res)->
#Explicitly handle uploads
busboy = new Busboy({ headers: req.headers })
busboy.on ‘file’, (fieldname, file, filename, encoding, mimetype)->
saveTo = path.join(process.cwd() + ‘/public/images/’, path.basename(fieldname) + path.extname(filename))
file.pipe(fs.createWriteStream(saveTo))
busboy.on ‘finish’, ()->
req.flash(‘success’, ‘文件上传成功!’);
res.redirect(’/’)
return req.pipe(busboy);
在Node.js应用中,如果你使用了CSRF(跨站请求伪造)保护,并且在处理包含文件上传的表单时遇到了问题,通常是因为multipart/form-data
编码格式导致CSRF令牌无法正确传递。为了解决这个问题,你可以通过修改你的中间件和表单处理逻辑来确保CSRF令牌能够被正确读取。
解决方案
- 确保CSRF中间件在其他中间件之前运行:确保你的CSRF中间件在其他可能修改请求体的中间件之前运行。
- 手动处理CSRF验证:对于
multipart/form-data
表单,你需要手动从请求中提取CSRF令牌并进行验证。
以下是一个示例代码,展示了如何实现上述解决方案:
const express = require('express');
const csrf = require('csurf');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const app = express();
// 使用cookie解析器和body解析器
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));
// 创建CSRF保护中间件
const csrfProtection = csrf({ cookie: true });
app.get('/', (req, res) => {
res.send(`
<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="hidden" name="_csrf" value="${req.csrfToken()}">
<input type="file" name="image">
<button type="submit">Upload</button>
</form>
`);
});
app.post('/upload', csrfProtection, (req, res) => {
const busboy = new Busboy({ headers: req.headers });
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
const saveTo = path.join(__dirname, 'public/images', path.basename(fieldname) + path.extname(filename));
file.pipe(fs.createWriteStream(saveTo));
});
busboy.on('finish', () => {
req.flash('success', '文件上传成功!');
res.redirect('/');
});
return req.pipe(busboy);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
解释
- 中间件顺序:确保
cookieParser
和bodyParser
在csrf
中间件之前运行,以确保CSRF令牌可以从cookies和请求体中正确读取。 - CSRF令牌嵌入表单:在表单中添加一个隐藏的输入字段
<input type="hidden" name="_csrf" value="${req.csrfToken()}">
,用于传递CSRF令牌。 - 手动处理CSRF验证:在处理文件上传的路由中,使用
csrfProtection
中间件来验证CSRF令牌。
通过这种方式,即使表单使用multipart/form-data
编码格式,你也能确保CSRF保护有效。
放到 http 头里是个办法
在使用 multipart/form-data
编码上传文件时,CSRF防护机制可能会失效,因为这种编码方式会改变请求的格式,导致内置的CSRF令牌验证失败。解决方法是手动处理文件上传,并在上传过程中确保CSRF令牌被正确传递和验证。
以下是解决方案的具体步骤:
- 使用
multer
作为文件上传中间件。 - 确保CSRF令牌被包含在HTML表单中。
- 在服务器端验证CSRF令牌。
示例代码:
const express = require('express');
const csrf = require('csurf');
const multer = require('multer');
const path = require('path');
const flash = require('connect-flash');
const app = express();
// 配置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 = csrf({ cookie: true });
app.use(express.urlencoded({ extended: true }));
app.use(flash());
app.get('/upload', csrfProtection, (req, res) => {
res.render('upload', { csrfToken: req.csrfToken() });
});
app.post('/upload', csrfProtection, upload.single('file'), (req, res) => {
req.flash('success', '文件上传成功!');
res.redirect('/');
});
app.listen(3000, () => console.log('Server started on port 3000'));
HTML 表单
确保表单包含 CSRF 令牌:
<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<input type="file" name="file">
<button type="submit">上传</button>
</form>
通过这种方式,即使表单设置了 enctype="multipart/form-data"
,也能确保CSRF令牌被正确传递并验证。