What is Insecure File Upload?
Your app has a profile picture upload. An attacker skips the frontend and sends a .html file containing a JavaScript payload directly to your upload endpoint. Your server saves it, serves it back on your domain, and now that script runs in every visitor's browser. That's insecure file upload.
The danger depends on what the server does with the file. If it stores and serves it as-is, an attacker can upload HTML (for XSS), SVGs with embedded scripts, or server-side scripts if the host executes them. Even "just" accepting huge files can take down your app.
CWE-434 covers all of these cases: the server fails to restrict what types of files can be uploaded, how large they can be, or where they end up. It's a deceptively simple bug with a wide blast radius.
How does Insecure File Upload work?
The typical pattern: the upload endpoint reads the file from the request, generates a filename, and writes it to storage — without checking what the file actually is. The client-side file picker might restrict to images, but an attacker never uses the file picker.
Here's a typical vulnerable upload handler:
// app/api/upload/route.ts
export async function POST(req) {
const formData = await req.formData();
const file = formData.get('file');
// Problem: accepts any file type, any size.
// An attacker can upload .html, .svg, or .exe.
const buffer = Buffer.from(await file.arrayBuffer());
const filename = file.name;
await storage.upload(`uploads/${filename}`, buffer);
return Response.json({
url: `/uploads/${filename}`,
});
}// app/api/upload/route.ts
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];
const ALLOWED_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.webp'];
const MAX_SIZE = 5 * 1024 * 1024; // 5 MB
export async function POST(req) {
const formData = await req.formData();
const file = formData.get('file');
// Check file size.
if (file.size > MAX_SIZE) {
return Response.json(
{ error: 'File too large' }, { status: 413 },
);
}
// Check MIME type and extension.
const ext = path.extname(file.name).toLowerCase();
if (
!ALLOWED_TYPES.includes(file.type) ||
!ALLOWED_EXTENSIONS.includes(ext)
) {
return Response.json(
{ error: 'File type not allowed' }, { status: 400 },
);
}
// Use a random filename — never trust the original.
const safeName = `${crypto.randomUUID()}${ext}`;
const buffer = Buffer.from(await file.arrayBuffer());
await storage.upload(`uploads/${safeName}`, buffer);
return Response.json({ url: `/uploads/${safeName}` });
}Why do AI tools generate Insecure File Upload vulnerabilities?
When you ask an AI to "add file upload," it generates the shortest path to a working upload. Validation, type checking, and size limits are extras it rarely includes unprompted.
- Tutorials skip validation for brevity. Most file upload examples in training data focus on getting the file saved. Validation is a "left as an exercise" footnote.
- Client-side restrictions feel like enough. The model might add <code>accept="image/*"</code> to the file input — but that only filters the file picker, not the API endpoint.
- Original filenames are used directly. Models rarely generate random filenames. They use <code>file.name</code> as-is, which opens the door to path traversal and file type spoofing.
A working upload and a safe upload are very different things. AI gets you the first one. The second one requires explicit constraints that models skip unless you ask.
Common Insecure File Upload patterns
No server-side type check
The frontend restricts to images, but the API accepts anything — .html, .svg, .php.
User-controlled filenames
Original filename saved as-is — enables path traversal (../../etc/passwd) and overwrites.
No size limit
Uploading a 2 GB file exhausts memory or disk, crashing the server for everyone.
Files served from same origin
Uploaded HTML/SVG files served on your domain execute JavaScript in the context of your app.
How Flowpatrol detects Insecure File Upload
Flowpatrol tests your upload endpoints the way an attacker would — by actually uploading dangerous files and checking what happens:
- 1Discovers upload endpoints by crawling your app and observing form submissions and API calls.
- 2Uploads test payloads — HTML files with script tags, SVGs with embedded JavaScript, files with spoofed extensions.
- 3Checks if the file is served back and whether the browser executes the payload when the uploaded file is accessed.
- 4Reports the full chain — upload request, stored file URL, proof of execution, and the fix.
Most scanners check for upload forms but never actually test them. Flowpatrol uploads, fetches, and verifies — proving the bug is real.
Related terms
Check your upload endpoints.
Flowpatrol tests what your file uploads actually accept. Paste your URL and find out.
Try it free