File upload endpoints have size and type restrictions
Why it matters
Unrestricted file uploads are the intersection of CWE-434 (unrestricted upload of dangerous file type) and CWE-770 (resource exhaustion). Accepting unbounded file sizes lets attackers fill your storage quota and exhaust memory parsing the upload body. Validating file type only by extension or the caller-controlled Content-Type header is trivially bypassed: renaming exploit.php to exploit.png and setting Content-Type: image/png defeats both checks. OWASP API 2023 flags this under API4 (unrestricted resource consumption) and API8 (security misconfiguration).
Severity rationale
High because malicious file uploads can exhaust storage, escape to arbitrary code execution if served directly, and bypass type checks that rely on attacker-controlled metadata.
Remediation
Harden every upload endpoint in app/api/ with three independent controls. First, limit size at the parser level. Second, validate MIME type from actual file bytes using the file-type package — Content-Type headers are attacker-controlled and cannot be trusted. Third, enforce an allowlist:
import { fileTypeFromBuffer } from 'file-type'
const MAX_SIZE = 5 * 1024 * 1024 // 5MB
const ALLOWED_TYPES = new Set(['image/jpeg', 'image/png', 'image/webp', 'application/pdf'])
const arrayBuffer = await file.arrayBuffer()
if (arrayBuffer.byteLength > MAX_SIZE) return apiError('FILE_TOO_LARGE', '...', 413)
const detected = await fileTypeFromBuffer(Buffer.from(arrayBuffer))
if (!detected || !ALLOWED_TYPES.has(detected.mime)) {
return apiError('INVALID_FILE_TYPE', '...', 415)
}
Store the file in object storage (S3, Supabase Storage, R2) under a random UUID key — never the user-supplied filename.
Detection
- ID:
file-upload-restrictions - Severity:
high - What to look for: Enumerate all API routes that handle file uploads (look for
multipart/form-datahandling,FormData,multer,formidable,busboy, direct file buffer handling, or Next.jsformData()calls). For each upload endpoint, check: (1) Is there a maximum file size limit enforced? (2) Is there a file type allowlist (MIME type check and/or file extension check)? (3) Is the MIME type validated from the actual file content, not just the filename or Content-Type header (which can be spoofed)? - Pass criteria: At least 100% of file upload endpoints enforce: an explicit maximum file size of no more than 10MB, a file type allowlist based on MIME type inspection (not just extension or Content-Type header alone), and rejection of disallowed types with a clear error response.
- Fail criteria: Any upload endpoint without a size limit; or type validation based only on file extension or Content-Type header (easily spoofed); or no type validation at all.
- Skip (N/A) when: No file upload functionality exists in the project. Signal: no
multipart/form-datahandling, nomulter/formidable/busboydependencies, noFormDataprocessing in any route handler. - Detail on fail: Example:
POST /api/upload has no file size limit. Identify which upload routes lack restrictions (e.g., "POST /api/upload has no file size limit; type is validated only by extension, not MIME content"). Max 500 chars. - Remediation: Harden every file upload endpoint in
app/api/with three controls. First, enforce a maximum file size at the parser level (e.g.,multer({ limits: { fileSize: 5 * 1024 * 1024 } })for 5MB). Second, validate the MIME type from the actual file content — use thefile-typenpm package which reads the file's magic bytes rather than trusting the declared Content-Type. Third, maintain an explicit allowlist of permitted types (e.g.,['image/jpeg', 'image/png', 'image/webp', 'application/pdf']) and reject anything not on it. Store uploaded files in cloud object storage (S3, Supabase Storage, Cloudflare R2) rather than the local filesystem, and give them random names (not user-supplied names) to prevent path traversal.
External references
- cwe · CWE-434 — Unrestricted Upload of File with Dangerous Type
- cwe · CWE-770 — Allocation of Resources Without Limits or Throttling
- owasp:2021 · A04 — Insecure Design
Taxons
History
- 2026-04-18·v1.0.0·Initial import from saas-api-design·automated