DEV Community

Cover image for File-Type Validation in Multer is NOT SAFEšŸ™ƒ
Ayanabilothman
Ayanabilothman

Posted on

File-Type Validation in Multer is NOT SAFEšŸ™ƒ

When dealing with file uploads in Node.js, one of the most used packages is Multer. Itā€™s simple to use, integrates well with Express, and allows for straightforward file handling. But unfortunately, Multerā€™s file-type validation is not secure enough šŸ˜“. By default, Multer does not validate the actual content of a file; it only depends on the file extension or the MIME type provided by the client, which is editable. The client can easily change the extension of the file in his operating system. This can lead to serious security vulnerabilities in your application.

The problemšŸ˜•

When a user uploads a file, Multer allows you to check the file's MIME type or extension to decide whether to accept it using the fileFilter option passed to multer. Hereā€™s a typical way to use Multer's fileFilter to check file types:

import multer from 'multer';

const fileFilter = (req, file, cb) => {
  if (file.mimetype === 'image/png' || file.mimetype === 'image/jpeg') {
    cb(null, true);
  } else {
    cb(new Error('Invalid file type, only PNG and JPEG is allowed!'), false);
  }
};

const upload = multer({
  dest: 'uploads',
  fileFilter,
});
Enter fullscreen mode Exit fullscreen mode

In the above example, Multer checks the MIME type of the file to ensure it's either a PNG or JPEG. This sounds pretty safe, right? Wrong šŸ˜. The MIME type that Multer checks is provided by the client, which means it's extremely easy to manipulate.

For example, a malicious user can rename a .exe file to .jpg and modify its MIME type to image/jpeg. Multer will happily accept this file, assuming itā€™s a valid image,šŸ™„ but in reality, the user has uploaded a potentially dangerous file.

The Inaccuracy of MIME Type Validation šŸ‘Ž

MIME types are just metadataā€”they donā€™t represent the actual content of the file. A determined attacker can easily bypass this by changing a fileā€™s extension or setting the wrong MIME type header in their request.

The following scenario can happen:

  • A user renames a harmful file virus.exe to image.jpg.
  • Then he sends the file to the server with the MIME type image/jpeg.
  • Multer accepts this file based on the MIME type.

This is why depending only on MIME type validation becomes dangerous.

The Solution šŸ¤—: Validate Using Magic Numbers (The file signature)

To accurately verify the content of a file, you need to inspect its magic numberā€”the unique binary signature that identifies the file type. Magic numbers are stored at the beginning of a file and are much harder to change than MIME types or extensions.

You can check for the magic number by manually writing some code on your own, but fortunately thereā€™s a great package for this in Node.js: file-type. It helps determine the actual file type by reading the first few bytes of the file, ensuring the file is genuinely what it claims to be.

file-type package has a method fileTypeFromBuffer that identifies the type of the file using the buffer format of the file. Since in the above code snippet, we store the file in the disk storage, we need a way to convert this file to buffer. This can be done easily using fs built-in package in Node.js.

You can implement express middleware to truly validate the file using the following code:

import { fileTypeFromBuffer } from "file-type";
import fs from "fs";

// Middleware to validate file type by magic number (file signatures)
export const fileValidation = async (req, res, next) => {
  try {
    // get the file path
    const filePath = req.file.path;
    // read the file and return buffer
    const buffer = fs.readFileSync(filePath);
    // get the file type
    const type = await fileTypeFromBuffer(buffer);
    // validate
    const allowedTypes = ["image/jpeg", "image/png"];
    if (!type || !allowedTypes.includes(type.mime))
      return next(new Error("Invalid file type"));

    return next();
  } catch (error) {
    return next(new Error("Internal server error"));
  }
};
Enter fullscreen mode Exit fullscreen mode

Hint
If you store your file in the memory, instead of disk storage, you can access the buffer data directly from req.file.buffer instead of using readFileSync

Why it is important? šŸ§

Security: Malicious users can easily bypass Multerā€™s built-in validation by renaming files and changing their MIME types. Using magic number validation ensures that youā€™re accepting only legitimate file types. If youā€™re dealing with sensitive data, ensuring file integrity is critical.
Reliability: While MIME type headers can be altered by users, magic numbers are tied to the fileā€™s content and cannot be easily faked.

So by inspecting the actual content of the file, you can prevent users from uploading dangerous files disguised as harmless ones, making your application safer and more secure.

Top comments (0)