A secure file upload feature depends on several controls applied together, and the OWASP File Upload Cheat Sheet lists them. This article goes through that list, points out the items that are easy to skip, and turns it into tests you can run against each upload endpoint.
What you are defending against
The cheat sheet groups the threats under "malicious files". The first is an attack on the software that handles the file. An attacker delivers a file that exploits a vulnerability in the parser or processing module, and the cheat sheet names the ImageTrick exploit and XXE as examples.
The second is phishing. The cheat sheet uses a careers form as its example, which means the upload becomes a way to put a hostile file in front of the people who open it.
The third is availability. ZIP bombs, XML bombs (the billion laughs attack) and simply huge files can fill the server storage and damage the availability of the server.
Validate before you accept
The cheat sheet sets a sequence: apply input validation before validating the extension. It then works from an allowlist.
- List allowed extensions. Permit only the safe ones the business function needs. A profile photo form has no reason to accept anything but a few image types.
- Validate the file type. Do not rely on the declared
Content-Type, because it can be spoofed. Validate the file type and signature before accepting the upload. - Set a filename length limit and restrict the allowed characters if possible.
- Set a file size limit.
If a file fails any of these checks, reject it.
Treat the name and the location with care
The cheat sheet says to change the user-supplied filename to something generated by the application. Do that for every upload, whatever the file type.
Where the file is stored matters as much as what it is. Store uploads on a different server if you can. If that is not possible, store them outside the webroot.
When files must be publicly reachable, use a handler inside the application that maps an identifier to a file (someid to file.ext) instead of exposing the storage path.
Two more items from the list are easy to forget:
- Only authorised users should be able to upload files.
- Protect the upload from CSRF attacks.
Scan and process what you keep
Validation checks that a file looks right. The cheat sheet adds a further step: run the file through an antivirus engine or a sandbox, if one is available, to validate that it contains no malicious data.
For applicable document types such as PDF and DOCX, consider CDR (Content Disarm & Reconstruct) as an additional processing step.
The cheat sheet also says that any libraries used must be securely configured and kept up to date. Given that parser vulnerabilities are one of the named threats, this is the item to assign to a named person with a review date.
Test each endpoint
The cheat sheet lists controls, not test cases. Every row below is our own derivation from its checklist, and the third column shows which item it comes from. Run the tests against every upload endpoint, including admin tools and the ones added for a single customer.
| Test | What a pass looks like | Basis |
|---|---|---|
| Upload a script file renamed to an allowed extension | Rejected after file type validation | Derived: validate the file type |
Send a disallowed file with an allowed Content-Type | Still rejected | Derived: do not trust the Content-Type header |
| Submit a very long filename | Rejected, or replaced by a generated name | Derived: filename length limit, generated filenames |
| Upload a file above the size limit | Refused | Derived: file size limit |
| Request the stored file by guessing its path | Not reachable except through the handler | Derived: storage location, mapped handler |
| Upload without a session, or without a CSRF token | Refused | Derived: authorised users, CSRF protection |
| Upload a ZIP or XML bomb in a test environment | The service stays available | Derived: availability threat |
For a wider reference, the OWASP Web Security Testing Guide describes itself as the premier cybersecurity testing resource for web application developers and security professionals. The OWASP Application Security Verification Standard provides numbered requirements to verify against. If you cite one in a ticket or report, include the version, for example v5.0.0-1.2.5, because identifiers may change between versions of the standard.
Where to start
- List every endpoint that accepts a file, including admin tools, import functions and old plugins.
- For each one, define the allowed extensions, the maximum file size and the maximum filename length, and apply input validation before the extension check.
- Replace user-supplied filenames with generated ones. Move storage to a separate server or outside the webroot, and serve public files through a handler that maps IDs to filenames.
- Add file type and signature validation, an antivirus or sandbox step, and CDR for the document types you accept. Check that parsing libraries are up to date and securely configured.
- Turn the test table into scripted checks and run them before each release. If you want an outside review of the result, our penetration testing service can test the endpoints, and our secure software development support can help if the feature is still at the design stage. The OWASP file upload guidance works as the review checklist either way.
