RestingOwl owl logo RestingOwl

Secure File Uploads:How to Handle User Files Safely

Quick answer: A file upload lets a user send a file to your server, and that is risky because the file is untrusted input. To handle uploads safely, check the file type and size, give the file a new random name, store it outside the web root or in object storage, and never let the server run it as code. Serve downloads through your application so you can control who gets which file.

File uploads feel simple. A user picks a file, your app saves it, and you show it later. But an upload is one of the most direct ways an attacker can reach your server, because the attacker fully controls the content and often the name of the file. This guide explains the real risks in plain language and gives you a clear checklist. It maps to the File Handling chapter of the OWASP standard, which you can explore in our ASVS Explorer.

Why Are File Uploads So Dangerous?

The danger comes from trust. When your app accepts a file, it accepts data that a stranger created. If any part of your system treats that file as safe, the attacker gains a foothold. A file can carry malware for other users to download. It can pretend to be an image while really being a script. It can have a name that points to a different folder and overwrite your own files. And if your server ever executes the file, the attacker can run their own code on your machine, which is the worst outcome in web security.

What Are the Main File Upload Attacks?

AttackHow it worksBest defense
Malicious file executionAttacker uploads a script (for example a .php or .jsp file) and then opens its URL, so the server runs it.Store files outside the web root and never serve them from a folder that can execute code.
Disguised file typeA file claims to be an image but its real content is a script or HTML that runs in the browser.Check the real content, not just the extension or the Content-Type sent by the browser.
Path traversalThe file name contains ../ or a full path, so the file lands somewhere it should not, or overwrites a real file.Ignore the sent name. Generate a new safe random name yourself.
Denial of serviceA very large file or many files fill your disk or memory and take the service down.Limit file size and the number of uploads per user and per request.
Stored XSS via filesAn uploaded HTML or SVG file runs scripts when another user views it.Serve user files with a download header and a restrictive Content Security Policy.

The disguised file type and stored script problems overlap with cross site scripting. If you accept images or documents that other users view, read our guide on cross site scripting as well.

How Do You Validate an Uploaded File?

Validation means checking that the file is what you expect before you keep it. Do all of these checks on the server, because anything the browser sends can be faked.

  • Check the size. Set a clear maximum size and reject anything larger before you read the whole file into memory.
  • Check the real type. Read the first bytes of the file (the magic number) to confirm the true format, instead of trusting the file extension or the Content-Type header.
  • Use an allowlist. Decide the small set of types you actually need, for example PNG and JPEG for avatars, and reject everything else. An allowlist is safer than a blocklist.
  • Rename the file. Create a new random name and keep the extension only if it matches an allowed type. Never reuse the name the user sent.
  • Scan when it matters. If users share files with each other, run an antivirus or malware scan before the file becomes available.

// Check the real type from the file bytes, not the name
const SIGNATURES = { png: [0x89,0x50,0x4e,0x47], jpg: [0xff,0xd8,0xff] };
function realType(buffer) {
  for (const [type, sig] of Object.entries(SIGNATURES))
    if (sig.every((b, i) => buffer[i] === b)) return type;
  return null; // unknown: reject the upload
}

Where Should You Store Uploaded Files?

The safest place for uploaded files is somewhere your web server cannot execute them. Two good options exist. First, store files in a folder outside the web root, so there is no direct URL that runs them. Second, and better for most modern apps, store them in object storage such as Amazon S3, Google Cloud Storage, or a similar service. Object storage serves files as plain data, keeps them off your application server, and scales without filling your disk. Whichever you choose, keep the file metadata (owner, type, size, real name) in your database, separate from the file content.

How Do You Serve File Downloads Safely?

Serving a file is the moment your careful storage can still go wrong. Do not expose a raw folder listing or predictable URLs that let anyone guess another user's files. Instead, route downloads through your application so you can check that the current user is allowed to read that file. When you send the file, set the header Content-Disposition: attachment so the browser downloads it rather than trying to display and run it, and send a strict Content Security Policy. For files that must be viewed inline, such as profile images, serve them from a separate domain or a locked down storage bucket so a malicious file cannot reach your main site's cookies.

Secure File Upload Checklist

File Upload Security Checklist
  1. 1Set and enforce a maximum file size and a maximum number of files per request.
  2. 2Validate the real file type from its content, using an allowlist of the types you need.
  3. 3Generate a new random file name and never trust the name the user sent.
  4. 4Store files outside the web root or in object storage, never in a folder that can run code.
  5. 5Keep file details in your database and check ownership before every download.
  6. 6Serve user files with Content-Disposition attachment and a strict Content Security Policy.
  7. 7Scan shared files for malware before other users can access them.
  8. 8Log uploads and downloads so you can investigate abuse.
Go deeper: File handling is chapter V5 of the OWASP ASVS. See how it fits with the rest of the standard in the ASVS Explorer, and pair these controls with a Content Security Policy to contain any file that slips through.

References

  1. 1OWASP File Upload Cheat Sheet
  2. 2OWASP ASVS 5.0: V5 File Handling
  3. 3MDN: Content-Type and MIME types

Q&A Section

No. The extension and the Content-Type header are both sent by the client and can be faked. An attacker can rename a script to end in .png. Always confirm the real file type by reading the first bytes of the file on the server, and combine that with an allowlist of the types you accept.
Usually no for the file content. Storing large files as database blobs makes the database slow and hard to back up. Store the file content in object storage or a folder outside the web root, and keep only the metadata, such as owner, type, size, and storage path, in the database.
The original name is attacker controlled. It can contain path characters that push the file into another folder, or a double extension that tricks your server into running it. Generating a new random name removes these tricks and also stops one user from guessing another user's files by name.
Not automatically. An SVG image can contain scripts, and a file can pretend to be an image while carrying HTML. Validate the real type, strip metadata where possible, and serve images with a download header or from a separate locked down domain so they cannot run scripts against your main site.
Copied!