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);


3 回复

在Node.js应用中,如果你使用了CSRF(跨站请求伪造)保护,并且在处理包含文件上传的表单时遇到了问题,通常是因为multipart/form-data编码格式导致CSRF令牌无法正确传递。为了解决这个问题,你可以通过修改你的中间件和表单处理逻辑来确保CSRF令牌能够被正确读取。

解决方案

  1. 确保CSRF中间件在其他中间件之前运行:确保你的CSRF中间件在其他可能修改请求体的中间件之前运行。
  2. 手动处理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');
});

解释

  1. 中间件顺序:确保cookieParserbodyParsercsrf中间件之前运行,以确保CSRF令牌可以从cookies和请求体中正确读取。
  2. CSRF令牌嵌入表单:在表单中添加一个隐藏的输入字段<input type="hidden" name="_csrf" value="${req.csrfToken()}">,用于传递CSRF令牌。
  3. 手动处理CSRF验证:在处理文件上传的路由中,使用csrfProtection中间件来验证CSRF令牌。

通过这种方式,即使表单使用multipart/form-data编码格式,你也能确保CSRF保护有效。


放到 http 头里是个办法

在使用 multipart/form-data 编码上传文件时,CSRF防护机制可能会失效,因为这种编码方式会改变请求的格式,导致内置的CSRF令牌验证失败。解决方法是手动处理文件上传,并在上传过程中确保CSRF令牌被正确传递和验证。

以下是解决方案的具体步骤:

  1. 使用 multer 作为文件上传中间件。
  2. 确保CSRF令牌被包含在HTML表单中。
  3. 在服务器端验证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令牌被正确传递并验证。

回到顶部