File Upload Handling
File uploads are a deceptively dangerous feature: they take attacker-controlled bytes and a filename and ask your server to store and later serve them. Done carelessly, they enable malware distribution, path traversal, stored XSS, and denial of service.
The guiding principle is simple — trust nothing the client tells you. Not the filename, not the MIME type, not the extension. Validate the actual content on the server, and store uploads where they can't be executed.
TL;DR
- Validate file type (by content), size, and count on the server — client checks are UX only.
- Never trust client-provided filenames or MIME types.
- Store uploads in object storage (or outside the web root); serve from a separate origin/CDN.
- Use signed URLs for direct-to-storage uploads at scale, and scan user content for malware.
Quick Example
Validate by sniffing the real bytes (magic number), not the client-supplied extension or MIME type:
Core Concepts
Validation
Also enforce size limits and a maximum file count to prevent denial of service.
Storage options
- Object storage (S3, GCS, Azure Blob) — the default at scale; durable and offloads bandwidth.
- Local filesystem — only outside the web root, with generated filenames.
- CDN in front for fast, cache-friendly delivery.
Security
Generate your own filenames (never reuse the client's), block path traversal (../), store outside any executable path, scan for malware, and enforce access control on private files. See Application Security.
Common Patterns
Direct-to-storage with signed URLs
At scale, don't proxy file bytes through your API — let the client upload straight to storage:
Large files
Use multipart or resumable uploads (chunk, upload in parallel, reassemble), and push thumbnailing/transcoding into background jobs.
Common Mistakes
Trusting the extension or MIME type
Serving uploads from your app's origin
FAQ
How do I validate file type securely?
Inspect the file's actual content (magic numbers) on the server rather than trusting the extension or Content-Type, both of which the client controls. For images, re-encoding to a known format is the strongest defense because it discards embedded payloads.
Should files go to my server or directly to storage?
For anything beyond small files, use signed URLs so clients upload directly to object storage. It removes your API as a bandwidth bottleneck and single point of failure; your backend only issues the URL and records metadata afterward.
Why not serve uploads from my main domain?
A malicious HTML or SVG file served from your origin can execute as stored XSS against your users. Serve user content from a separate origin/CDN, force downloads with Content-Disposition, and apply a strict CSP.
Do I need virus scanning?
For any user-to-user file sharing, yes — scan uploads (e.g. ClamAV or a cloud scanning service) before making them available, ideally in a background job after the upload completes.
Related Topics
- AWS S3 — Object storage and signed URLs
- Application Security — The broader threat model
- Cross-Site Scripting (XSS) — Why upload origin matters
- Background Jobs — Thumbnailing and scanning off the request path