OWASP File Upload Security
Profile pictures, résumés, invoices, and support attachments make applications useful. Each upload also carries risk. A file that looks like a picture may contain a script, a huge file may crash the server, and a clever file name may overwrite important data. Safe upload handling treats every file as untrusted.
The Mailroom Comparison
A company mailroom opens no package blindly. Staff check the sender, inspect the box, weigh it, and send it to the right desk. A secure upload feature follows the same routine for files.
What Can Go Wrong
| Risk | Plain Meaning |
|---|---|
| Executable upload | An uploaded script runs on the server when someone opens its address |
| Stored script content | An uploaded page or image runs scripts in visitors' browsers |
| Path tricks | A file name such as ../../config overwrites files outside the upload folder |
| Oversized files | Large uploads fill the disk or memory |
| Malware | Infected documents spread to other users |
| Decompression bombs | A small archive expands into enormous data |
Safe Upload Pipeline
User selects file
|
↓
[1] Size limit check
|
↓
[2] Extension allowlist check (.jpg .png .pdf)
|
↓
[3] Content check (file signature matches claimed type)
|
↓
[4] Malware scan
|
↓
[5] Rename with random name
|
↓
[6] Store outside web root or in object storage
|
↓
[7] Serve through controlled download with safe headers
Step-by-Step Guidance
Allow Only Needed Types
Define the exact extensions the feature requires. A profile picture form accepts JPG and PNG and nothing else.
Verify the Content
The extension and the declared content type come from the user and can lie. Read the first bytes of the file, known as the file signature or magic bytes, and confirm they match the claimed type. Re-encode images with a trusted library to strip hidden data.
Generate New File Names
Ignore the user's file name. Create a random name, such as a long unique identifier, and keep the original name in the database for display only. Random names stop path tricks and overwrites.
Store Files Safely
Place uploads outside the folder that the web server serves directly, or use cloud object storage. Remove execute permission from the upload directory.
Control Delivery
Serve files with the correct content type, the X-Content-Type-Options: nosniff header, and a Content-Disposition: attachment header when the browser should download instead of display. Host user files on a separate domain so that any script inside them cannot reach your main site cookies.
Code Pattern
import uuid, os
ALLOWED = {".jpg", ".jpeg", ".png"}
MAX_BYTES = 2 * 1024 * 1024
def save_upload(file_bytes, original_name):
ext = os.path.splitext(original_name)[1].lower()
if ext not in ALLOWED or len(file_bytes) > MAX_BYTES:
raise ValueError("File rejected")
new_name = uuid.uuid4().hex + ext
with open(os.path.join("/data/uploads", new_name), "wb") as f:
f.write(file_bytes)
return new_name
Extra Safeguards
- Require login before upload and limit the number of uploads per user.
- Scan archives with limits on depth and expanded size.
- Log every upload with the user, time, and file hash.
- Delete unused files on a schedule.
Key Takeaways
- Every uploaded file is untrusted input.
- Allowlists, content checks, and random names block the most common attacks.
- Safe storage and controlled delivery stop uploaded files from running.
- Size limits and scanning protect resources and other users.
