Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to validate file extension with Multer middleware

I use Multer for uploading file or images. Problem is that it is impossible to validate the real file extension.

Example: If someone rename filename.exe to filename.png, then it is still validate to upload.

Can you suggest me the solution to handle this issue? Thanks

I used like this but need verify real ext of files

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); 
  },
like image 521
Hai Tien Avatar asked Feb 26 '20 07:02

Hai Tien


3 Answers

Starting with Multer 2.x.x You can check for both the extension and the MIME type of the uploaded parameter. Here is a sample code.

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 {
    cb('Error: Images Only!');
  }
}

Don't forget to check your file permissions too. You don't want the uploaded file to be executed somehow. Sadly the latest npm release is 1.4.3 where the Multer mime type is based on the type property that comes from the client. That property at least in windows is dependent of the file extension, not on the file content.

like image 187
Fatih Aktaş Avatar answered Oct 22 '22 12:10

Fatih Aktaş


There are two steps:

  1. Filtering by mime type provided by multer on fileFilter options
  2. Filtering the real mime type from the file buffer/stream using file-type.

Define whitelist mime type:

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

Example of the first step:

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

Example of the second step:

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,
  })
})

I tried to rename a document.pdf -> document.png and it passed the first check but got caught on the second one.

like image 30
Nurul Huda Avatar answered Oct 22 '22 13:10

Nurul Huda


Basically what you need is something which can verify the extension with original file type.

Copied from a blog

    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!'));
    }
  }
});

Blog Link

If this doesn't work for you, consider using a separate module which verify file type from buffer data.

like image 44
Gandalf the White Avatar answered Oct 22 '22 12:10

Gandalf the White