Fix Insecure File Uploads in Vibe-Coded Apps: A Step-by-Step Guide
OverMCP Team
Quick answer
Insecure file uploads happen when vibe-coded apps fail to validate file types, limit sizes, or scan content. To fix this, restrict uploads to safe extensions, enforce server-side validation, store files outside the webroot, and use a free AI app security scanner like OverMCP to catch leaks before launch.
What to check first
Before you start coding fixes, audit your current upload implementation. Here's a quick checklist:
/public/uploads)?Run a free AI app security scanner to automatically detect exposed upload directories and leaked secrets.
Step-by-step fix
Let's walk through securing a file upload endpoint in a typical Next.js app built with Cursor or v0.
1. Validate file type server-side
AI-generated upload handlers often trust the client's Content-Type. Always re-check on the server.
// pages/api/upload.js
import multer from 'multer';
import path from 'path';
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
const storage = multer.diskStorage({
destination: './uploads',
filename: (req, file, cb) => {
// Sanitize filename: remove path separators and special chars
const safeName = file.originalname.replace(/[^a-zA-Z0-9._-]/g, '_');
cb(null, `${Date.now()}-${safeName}`);
}
});
const upload = multer({
storage,
limits: { fileSize: MAX_SIZE },
fileFilter: (req, file, cb) => {
if (ALLOWED_TYPES.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('Invalid file type'));
}
}
});
export default function handler(req, res) {
upload.single('file')(req, res, (err) => {
if (err) return res.status(400).json({ error: err.message });
res.status(200).json({ message: 'Upload successful' });
});
}2. Store files outside the webroot
Never store uploads inside public/. Instead, use a dedicated directory and serve files via a controlled endpoint.
// Example: serve file through an API route
import fs from 'fs';
import path from 'path';
export default function handler(req, res) {
const filePath = path.join(process.cwd(), 'uploads', req.query.filename);
if (!fs.existsSync(filePath)) return res.status(404).end();
// Set appropriate Content-Type and security headers
res.setHeader('Content-Type', 'image/png');
res.setHeader('Content-Disposition', 'inline');
res.setHeader('X-Content-Type-Options', 'nosniff');
fs.createReadStream(filePath).pipe(res);
}3. Add security headers to upload responses
Prevent MIME sniffing and other attacks.
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('Cache-Control', 'no-store');4. Scan for secrets in uploaded files
Use a secret leak scanner to check if any uploaded file contains API keys or credentials.
5. Enforce authentication and rate limiting
Wrap your upload handler with authentication middleware and use a rate limiter.
import rateLimit from 'express-rate-limit';
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 10, // limit each IP to 10 uploads per window
});
export default async function handler(req, res) {
await new Promise((resolve) => limiter(req, res, resolve));
// ... rest of upload logic
}Common mistakes
Vibe-coded apps often make these mistakes with file uploads:
accept="image/*" attribute but forgets server-side checks. Attackers can bypass this easily./public/uploads, making them directly accessible and executable.../../etc/passwd can lead to path traversal if not sanitized..jpg, the content could be a PHP script or malware.To catch these issues before they become problems, use continuous security monitoring to scan your app after every deploy.
FAQ
What is the most dangerous file upload vulnerability?
Storing files in a publicly accessible directory without proper type validation can allow attackers to upload executable scripts (e.g., PHP, JS) and run them on your server. This can lead to full server compromise.
How do I test if my upload endpoint is secure?
Try uploading a file with a fake extension (e.g., malicious.php.jpg) and check if it's saved with the original name. Use a security headers checker to verify response headers. Also, attempt a path traversal in the filename.
Can I trust AI-generated upload code?
No. AI tools like Cursor and v0 often generate minimal, insecure upload handlers. Always audit the code for the common mistakes listed above. Use OverMCP to scan your entire app for file upload vulnerabilities and other OWASP Top 10 issues.