Skip to content
ShipSecurelySecurity reference
Menu
Your tool here — sponsor ShipSecurely

Issues

Unsafe file uploads

An upload that trusts the browser's file type, keeps the original name, or serves the file from your site can become stored XSS or a malware host.

Share link · Share on X

Last updated

highInjection

Shows up in: Supabase, Firebase, Next.js, Vercel

Markdown version

What it is

Upload bugs are a cluster, not one mistake.

The browser sends a filename and a content type. Both are chosen by the client. An agent often checks file.type in React and then stores whatever arrived. The server has to enforce size and type again.

SVG and HTML files are scripts if you serve them from your origin with a browser content type. A user uploads avatar.svg, you put it on your domain, and everyone who views the avatar runs the SVG.

Original filenames with ../ or a path can overwrite something else if you join them onto a directory. Generated names avoid that.

A public bucket is a separate bug. See open storage buckets. Even a private bucket is unsafe if the policy lets any signed-in user read every object.

How to check in 5 minutes

  1. Find the upload handler. Confirm size and type are checked on the server, not only in the file input's accept attribute.

  2. See whether the stored name is the user's filename. If it is, that is a path and content-type problem.

  3. Upload an SVG or HTML file to a test account you own and open the file URL. If it renders as a page on your domain, browsers will run script in it.

  4. Check the storage policy or bucket flag. If anyone who can guess or list the URL can read other users' files, fix that with the open-bucket guide.

How to fix

Next.js

Limit size before you buffer the whole file. Allow a short list of types you actually need (for example PNG, JPEG, WebP). Detect type from the bytes if you can, and still do not trust the extension alone.

Store a generated name (userId/random-id), never the raw filename. Serve user files from a different host when you can, or with Content-Disposition: attachment and X-Content-Type-Options: nosniff. Do not serve user SVG or HTML as an image preview on your origin.

Supabase

Use a private bucket and storage policies that mirror table RLS: a user can upload and read objects under their own prefix. Reject other prefixes in the policy. Supabase storage is not covered by a table's RLS. The storage policy is the control.

Set a file size limit in the bucket settings and again in the route that creates the signed upload if you have one.

Firebase

Storage rules should require request.auth.uid to match the path, limit request.resource.size, and check request.resource.contentType against your list. allow read, write: if true is the open-bucket bug.

Vercel

If you use Vercel Blob or similar, the same rules apply: private by default, random names, size cap, and do not redirect users to a same-site HTML upload. Check that product's current docs for the exact ACL field. The principle does not depend on the vendor.

Copy-paste prompt

Paste this into Cursor, Claude Code, or whichever agent is editing the repo. Read the diff before you merge it.

You are tightening file uploads. Do not add features.

Find every upload path, including direct-to-bucket uploads.

1. Enforce a maximum size on the server or in the storage policy.
2. Allow only the content types this product needs. Do not allow SVG or HTML unless I said the product needs them, and do not serve those from this site's origin.
3. Store a generated object name. Do not use the user-supplied filename as the path.
4. Make the bucket private. Add a read policy so a user can only read their own prefix. Do not leave the bucket public to make previews easy. Use a short-lived signed URL instead.
5. Set nosniff on responses you control.

Show the policy or handler you changed and how a second user is blocked from reading the file.

Questions

The input accept attribute is image/*. Is that the check?

No. That hint is for the file dialog. The request can send any bytes. Check on the server or in storage rules.

Can I rename the file to .png and be done?

No. The bytes and the content type you send when someone downloads it decide how the browser treats it. A script renamed to .png and served as text/html is still HTML.

Sources

Related