如何使用Multer中间件验证文件扩展名

16

我使用Multer来上传文件或图像。问题是无法验证实际的文件扩展名。

例如:如果有人将filename.exe重命名为filename.png,那么它仍然可以通过验证上传。

你能建议我处理这个问题的解决方案吗?谢谢

我像这样使用但需要验证文件的真实扩展名

fileFilter: async function (req, file, callback) {
    var ext = path.extname(file.originalname);
    if(ext !== '.png' && ext !== '.jpg' && ext !== '.gif' && ext !== '.jpeg' && ext !== '.zip') {
        return callback(new Error('Only images and zip are allowed'));
    }
    // I want next function to validate real ext of files here. 
    callback(null, true); 
  },
4个回答

21

从Multer 2.x.x开始,您可以检查上传参数的扩展名和MIME类型。以下是示例代码。

const storage = multer.diskStorage({
    destination: './uploadedContent',
    filename: function(_req, file, cb){
      
      cb(null,file.fieldname + '-' + Date.now() + path.extname(file.originalname));
    } 
  });
var upload = multer({
    storage: storage,
    limits: {
        fields: 5,
        fieldNameSize: 50, // TODO: Check if this size is enough
        fieldSize: 20000, //TODO: Check if this size is enough
        // TODO: Change this line after compression
        fileSize: 15000000, // 150 KB for a 1080x1080 JPG 90
    },
    fileFilter: function(_req, file, cb){
        checkFileType(file, cb);
    }
}).single('postPicture');
function checkFileType(file, cb){
  // Allowed ext
  const filetypes = /jpeg|jpg|png|gif/;
  // Check ext
  const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
  // Check mime
  const mimetype = filetypes.test(file.mimetype);

  if(mimetype && extname){
    return cb(null, true);
  } else {
    return cb(null, false);
  }
}

别忘了检查文件权限。不要让上传的文件以某种方式被执行。 不幸的是,最新的npm发布版本是1.4.3,其中Multer MIME类型基于来自客户端的type属性。该属性至少在Windows上取决于文件扩展名,而不是文件内容。


2
你只能在Multer 2.x.x版本中这样做,但当前可用的npm版本1.4.3仅依赖于文件类型头。并且在大多数系统中基于文件扩展名。因此,目前检查MIME类型无法帮助您进行验证。您必须使用file-type来完成它。 - Julio Spinelli
@JulioSpinelli 你所说的话真的很重要,值得我们深思。 - Luis

8

有两个步骤:

  1. 使用 multer 的 fileFilter 选项提供的 mime 类型进行过滤。
  2. 使用 file-type 从文件缓冲区/流中过滤出真正的 mime 类型。

定义白名单的 mime 类型:

const whitelist = [
  'image/png',
  'image/jpeg',
  'image/jpg',
  'image/webp'
]

第一步示例:

const upload = multer({
  storage: multer.diskStorage({
    destination: 'public/uploads/',
    filename: (req, file, cb) => {
      const name = slugify(file.originalname, { lower: true })
      cb(null, `${new Date().getTime()}-${name}`)
    },
  }),
  fileFilter: (req, file, cb) => {
    if (!whitelist.includes(file.mimetype)) {
      return cb(new Error('file is not allowed'))
    }

    cb(null, true)
  }
})

第二步示例:

const FileType = require('file-type')

// ....
// first step
// ....

/**
 * Second step
 */
app.use('store', upload.single('image'), async (req, res, next) => {
  const meta = await FileType.fromFile(req.file.path)

  if (!whitelist.includes(meta.mime)) {
    return next(new Error('file is not allowed'))
  }

  res.json({
    file: req.file,
    body: req.body,
  })
})

我试图将一个 document.pdf 重命名为 document.png,第一次检查通过了,但被第二次检查抓住了。


这是最佳答案,因为两步验证提供了最高的安全性。顺便提一下,在fileFilter函数中无法检查FileType,因为文件(或缓冲区)在那个步骤还不可用。 - Daniel Nitu
2
文件类型现在要求项目必须是纯ESM。BOOOOO - Alan Spurlock
上传的文件没有通过第二次测试,你会怎么处理?你会定期运行CRON作业来删除它们吗? - Florian Walther
1
在第二个测试抛出异常之前,实际上可以直接删除文件。 - Nurul Huda

4
基本上,您需要的是可以验证扩展名与原始文件类型匹配的工具。
从博客复制。
    var upload = multer({
  storage: storage,
  fileFilter: (req, file, cb) => {
    if (file.mimetype == "image/png" || file.mimetype == "image/jpg" || file.mimetype == "image/jpeg") {
      cb(null, true);
    } else {
      cb(null, false);
      return cb(new Error('Only .png, .jpg and .jpeg format allowed!'));
    }
  }
});

如果这对你不起作用,请考虑使用一个单独的模块,从缓冲数据中验证文件类型。

博客链接


-1

基本上你需要的是一个可以验证扩展名与原始文件类型的工具。

摘自博客

var upload = multer({
  storage: storage,
  fileFilter: (req, file, cb) => {
    if (file.mimetype == "image/png" || file.mimetype == "image/jpg" || file.mimetype == "image/jpeg") {
      cb(null, true);
    } else {
      cb(null, false);
      return cb(new Error('Only .png, .jpg and .jpeg format allowed!'));
    }
  }
});

博客链接

如果这对你不起作用,考虑使用一个单独的模块来验证缓冲数据中的文件类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接