Safe file uploads with multer

File uploads are one of the most exploited corners of any web API. Accept a file the wrong way and you hand attackers the keys. Get it right with a handful of defensive rules and the feature is safe.

after/middlewares/upload.ts
typescript
import multer from 'multer';
import path from 'path';
import crypto from 'crypto';

const storage = multer.diskStorage({
  destination: 'uploads/',
  filename: (req, file, cb) => {
    // Never trust the client-provided filename
    const random = crypto.randomBytes(8).toString('hex');
    const ext = path.extname(file.originalname).toLowerCase();
    cb(null, random + ext);
  },
});

const allowed = new Set(['image/jpeg', 'image/png', 'image/webp']);

export const upload = multer({
  storage,
  limits: { fileSize: 5 * 1024 * 1024 }, // 5 MB
  fileFilter: (req, file, cb) => {
    if (allowed.has(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error('Unsupported file type'));
    }
  },
});

Multer configured with a safe filename, MIME type check, and size limit.

Never trust file.originalname. Attackers can include slashes and dots to escape the upload directory: ../../etc/passwd. Generating your own random filename eliminates that entire attack class. The extension comes from the original file, but even that is untrusted at a deeper level, which is why the MIME type check exists too.

The size limit prevents memory exhaustion attacks. Without it, an attacker can upload a 10 GB file and your server runs out of RAM. 5 MB is a reasonable default for images. Adjust it per endpoint: larger for videos, smaller for avatars.

Quiz: Quiz

Loading practice…