Upload forms look harmless: a CV on a careers page, product images in an admin panel, a lab report in a patient portal, a 3D model on a marketplace. Yet file uploads are one of the most frequently abused features in web applications. A single mistake can let an attacker run code on your server, host malware on your domain, or read files that belong to other customers. This guide covers secure file upload design in practical terms: the mistakes we regularly find in audits, and a layered approach that fixes them.
Why uploads are so risky
An upload feature accepts arbitrary binary content from the internet and stores it on your infrastructure. Depending on how it is built, that content may later be:
- Executed by the web server.
- Opened by staff on their computers.
- Processed by image, PDF or document libraries.
- Served to other users from your domain.
Each of those is an opportunity for something to go wrong, and each needs its own control.
The most common mistakes
1. Trusting the extension or MIME type
The file name and the Content-Type header both come from the user. Renaming shell.php to shell.php.jpg, or sending a PHP file with an image MIME type, defeats checks that rely on them alone.
2. Storing uploads inside the web root
If files land in a public folder and the server will execute scripts there, an uploaded script becomes a web shell: a remote control for your server. This is one of the most common causes of compromise on PHP sites, including WordPress.
3. Keeping the original file name
User-supplied names can include path traversal sequences such as ../, special characters, or names that overwrite existing files. They also leak personal information when exposed in URLs.
4. Public URLs for private files
Invoices, ID scans and medical documents placed at predictable public URLs can be discovered or guessed. Changing a number in the URL should never reveal another customer's file; this is the broken access control risk described in our OWASP Top 10 plain-language guide.
5. No size or rate limits
Without limits, uploads can fill disks, exhaust memory during processing or be used to run up storage costs.
6. Unsafe processing
Image and document libraries parse complex formats and have had their own vulnerabilities. Archive files can contain huge decompressed payloads ("zip bombs") or paths that write outside the intended folder.
7. Serving files in a way browsers execute
An uploaded HTML or SVG file served from your main domain can run scripts in your visitors' browsers in the context of your site, which enables cross-site scripting.
Key takeaway: Never assume an uploaded file is what it claims to be. Validate it, rename it, store it where it cannot run, and serve it through your own access checks.
A layered approach to secure file upload
No single check is enough. The following layers work together.
Layer 1: decide what you actually accept
Start with business requirements. A careers form may need PDF and DOCX only. A product catalogue needs JPEG, PNG and WebP. A jewellery 3D marketplace may need STL or similar model formats. Write an explicit allow-list per feature; never use a deny-list of "dangerous" types.
Layer 2: validate on the server
- Check the extension against the allow-list.
- Inspect the file content (magic bytes or a reliable MIME detection library) and confirm it matches an allowed type.
- Enforce a maximum size per feature at both the web server and application level.
- For images, check dimensions and consider re-encoding them, which strips embedded payloads and metadata.
- For archives, limit the number of entries and total decompressed size, and reject paths that escape the target folder.
In Laravel, validation rules such as mimes, mimetypes, max and dimensions cover much of this, provided they are applied on every upload endpoint, including API routes.
Layer 3: store safely
| Practice | Why |
|---|---|
| Store outside the public web root, or in private object storage | Files cannot be requested or executed directly |
| Generate a random file name on save | Prevents overwriting, traversal and information leaks |
| Keep the original name only as metadata | Displayed safely, never used as a path |
| Disable script execution in any upload directory | Defence in depth if a file ends up public |
| Encrypt sensitive files at rest | Protects data if storage or backups leak |
| Separate storage per tenant or customer where relevant | Limits the impact of access bugs |
Layer 4: serve through access control
- Route downloads through your application, checking that the logged-in user is allowed to access that specific file.
- Use short-lived signed URLs for object storage instead of permanent public links.
- Set
Content-Disposition: attachmentfor documents users should download rather than view. - Send
X-Content-Type-Options: nosniffand an accurateContent-Type. - Serve user content from a separate domain or subdomain where practical, so any script in it cannot access your main site's cookies.
- Log access to sensitive files.
Layer 5: scan and process in isolation
- Run antivirus or malware scanning on uploads that staff or other users will open.
- Process images and documents in background jobs with time and memory limits.
- Keep processing libraries updated; they are part of your supply chain.
- Quarantine files until scanning completes for high-risk workflows.
Layer 6: limit and monitor
- Rate-limit uploads per user and per IP.
- Add bot protection to anonymous upload forms, such as career applications. See bot protection and CAPTCHA alternatives.
- Monitor storage growth and unusual upload patterns.
- Alert on files appearing in places they should not, such as executable files in storage folders.
Special cases
Sensitive personal documents
Identity documents, medical reports and financial statements carry legal obligations under privacy laws such as PIPEDA, BC PIPA, the UAE PDPL and the GDPR. Collect them only when necessary, restrict access by role, encrypt them, define retention periods and delete them on schedule. Our guide to protecting health data in web applications covers this in more depth.
Paid digital files
Marketplaces selling downloadable files, from design assets to 3D models, need private storage, signed expiring download links, download limits per purchase and watermarking or licensing where appropriate. Our T-Rex commerce engine uses this pattern for digital product delivery.
Admin uploads
Uploads by staff deserve the same validation. A compromised admin account or a malicious file passed on by a supplier can be just as harmful.
A quick checklist for your team
- Is there an allow-list of file types for each upload feature?
- Is file content checked on the server, not just the extension?
- Are files renamed and stored outside the public web root?
- Is script execution disabled in every upload location?
- Do downloads go through permission checks or signed, expiring URLs?
- Are size, count and rate limits enforced?
- Are sensitive files encrypted, logged and deleted on schedule?
- Are processing libraries kept up to date?
If any answer is "not sure", that is where to look first.
Testing your upload features
Upload handling is easy to get right in one place and wrong in another, so test every endpoint, including API routes and admin screens. A simple test plan includes trying a script file renamed with an image extension, a file with a double extension, an oversized file, a file name containing path characters, and requesting another user's file by changing its identifier. Each should be rejected or denied cleanly, with a helpful error and a log entry.
Next steps
Uploads deserve a focused review in any platform that accepts files from the public or from customers. Most fixes are contained changes, and the risk reduction is significant.
DigiVort designs upload handling into the web applications and SaaS platforms we build and reviews existing systems through our security and compliance service. If you are planning a portal or marketplace that handles files, start a project and we will include a secure upload design from the outset.


