Secure File Uploads in MERN (MongoDB, Express, React, Node)
File uploads are a high-risk feature. Attackers can upload:
Malware
Scripts (XSS, RCE payloads)
Oversized files (DoS)
Poisoned images or PDFs
Wrong MIME types
Files with misleading extensions
To secure a MERN upload pipeline, you must validate, sanitize, scan, and isolate uploaded files.
๐งฑ 1. Frontend (React) File Validation
Frontend validation is not security, but improves UX. Backend still does all enforcement.
React example:
const allowedTypes = ["image/jpeg", "image/png"];
const maxSize = 5 * 1024 * 1024; // 5MB
function handleFile(e) {
const file = e.target.files[0];
if (!allowedTypes.includes(file.type)) {
alert("Only JPEG/PNG allowed.");
return;
}
if (file.size > maxSize) {
alert("File is too large.");
return;
}
}
✔ UX validation
✘ NOT a security control (still bypassable)
๐งฉ 2. Backend Upload Security (Node + Express)
Use Multer or Busboy for parsing uploads — but wrap them in secure configs.
Install Multer
npm install multer
2.1 Multer Secure Configuration
const multer = require("multer");
const path = require("path");
const storage = multer.memoryStorage(); // safer than diskStorage for validation
const upload = multer({
storage,
limits: {
fileSize: 5 * 1024 * 1024, // 5MB
},
fileFilter: (req, file, cb) => {
const ext = path.extname(file.originalname).toLowerCase();
const allowed = [".jpg", ".jpeg", ".png", ".pdf"];
if (!allowed.includes(ext)) {
return cb(new Error("Invalid file type"), false);
}
if (!file.mimetype.startsWith("image/") && ext !== ".pdf") {
return cb(new Error("MIME type mismatch"), false);
}
cb(null, true);
},
});
Why memoryStorage?
Prevents writing malicious files to disk before checks
Ideal when you plan to upload to cloud storage (S3, Cloudinary, Firebase)
๐งผ 2.2 Sanitizing File Names
Attackers can poison filenames:
const sanitizeFilename = require("sanitize-filename");
const safeName = sanitizeFilename(file.originalname).replace(/\s+/g, "_");
Never trust the original filename.
๐ 2.3 Block Dangerous File Types
Even if disguised:
.js, .exe, .php, .html, .svg
Double extensions (image.png.exe, file.jpg.php)
Files with null bytes (file.jpg\0.exe)
๐ก️ 2.4 Virus/Malicious Content Scanning (Optional but Strongly Recommended)
Use ClamAV, Cloud antivirus APIs, or VirusTotal:
npm install clamscan
Example:
const NodeClam = require('clamscan');
const clamscan = await new NodeClam().init();
const { is_infected } = await clamscan.scan_buf(file.buffer);
if (is_infected) throw new Error("Malicious file detected");
☁️ 3. Store Files Safely (Cloud Recommended)
Never store untrusted uploads inside your app directory
If an attacker uploads malware.php, they might execute it if served publicly.
Safer options:
AWS S3 with private buckets
Cloudinary
Firebase Storage
Azure Blob Storage
AWS S3 Upload Example
const AWS = require("aws-sdk");
const s3 = new AWS.S3();
const params = {
Bucket: process.env.S3_BUCKET,
Key: safeName,
Body: file.buffer,
ContentType: file.mimetype,
ACL: "private",
};
await s3.upload(params).promise();
✔ No executable permissions
✔ Versioning
✔ Access control
✔ Scalable
๐งฑ 4. MongoDB Security
Do NOT store raw file binaries in MongoDB unless necessary. Prefer cloud storage, store only metadata:
{
filename: safeName,
url: s3Url,
size: file.size,
uploadedBy: userId,
createdAt: Date.now()
}
If using GridFS:
Set strict file size limits
Validate metadata before saving
๐ 5. Endpoint Hardening
✔ Rate limit uploads
const rateLimit = require("express-rate-limit");
app.use("/upload", rateLimit({
windowMs: 10 * 60 * 1000,
max: 20, // 20 uploads per 10 min
}));
✔ Validate user authentication before upload
JWT
OAuth
Session auth
✔ Enforce API-level validation
Required user roles
File purpose constraints
๐งณ 6. Serve Files Securely
1. Signed URLs (Best Practice)
Let clients download files via expiring URLs.
2. Never serve files directly from a writable directory
This prevents arbitrary code execution.
3. Remove EXIF metadata
Prevents location leaks for uploaded photos.
๐ 7. Production Best Practices
✔ Limit max upload size at all layers:
React
Express (body size limit)
NGINX/Apache reverse proxy
Cloud storage
✔ Disable file execution in upload directories
If using a local upload directory:
On Linux: set noexec mount option
Block running .js, .php, .py, etc.
✔ Validate images with a library (sharp / imagemagick)
To prevent “polyglot” file attacks.
const sharp = require("sharp");
await sharp(file.buffer).metadata(); // throws on invalid image
✔ Use HTTPS for all uploads
Avoid on-path tampering.
๐งช 8. End-to-End Secure Upload Flow (Recommended)
React:
↓ Validate type and size
Express:
↓ Multer with memoryStorage
↓ Validate extension + mime
↓ Sanitize filename
↓ virus scan (optional)
↓ validate authentic image (sharp)
↓ upload to S3 / Cloud storage
MongoDB:
↓ store metadata only
Client:
↓ access using signed URL
This is the industry-standard MERN upload architecture.
๐ Summary: Essential Security Controls
Security Layer Purpose
File type/size validation Prevent unwanted files
Filename sanitization Prevent path/command injection
MIME sniffing Block file type spoofing
Virus scanning Malware prevention
Cloud storage Isolation + access control
Rate limiting Prevent DoS
Auth checks Prevent unauthorized uploads
Metadata-only in Mongo Prevent DB bloat & exploitation
This combination gives you a production-grade secure upload pipeline.
Learn MERN Stack Training in Hyderabad
Read More
Rate Limiting & Throttling with Express
Using Helmet for Express Security
Storing Passwords Securely with bcrypt
Role-Based Access Control in MERN Stack
Visit Our Quality Thought Training Institute in Hyderabad
Subscribe by Email
Follow Updates Articles from This Blog via Email
No Comments