Security

    Secure file uploads in mobile apps: what to check

    A phone uploading a photo through a short-lived signed link into a private storage bucket, with a server step that strips location metadata.

    Almost every mobile app eventually lets users upload something: a profile photo, a receipt, an identity document, a voice note, a PDF. Uploads look like a storage problem and behave like a security problem, because the file comes from a device you do not control and ends up somewhere your other users, your staff or your servers will open it. The weaknesses are well known, and most of them sit on the backend side of the upload rather than in the app.

    Short answer

    Treat every uploaded file as untrusted input. Upload through a short-lived, narrowly scoped URL or an authenticated endpoint rather than a shared credential in the app, validate type and size on the server, store files privately with random names, serve them only through access-checked links, strip metadata from photos, and scan files that other people will open. The app can check type and size for a better experience, and none of those checks count as security, because the request can be sent without the app at all.

    Why uploads are a security problem

    An upload is a request containing bytes that someone else chose. Those bytes can be a different type than the extension claims, far larger than expected, crafted to exploit a parser, carrying hidden metadata, or simply someone else's private document if access controls are wrong. CWE-434, unrestricted upload of file with dangerous type describes the classic server-side version, and the OWASP File Upload Cheat Sheet lists the controls in detail.

    For mobile apps the threat picture has a particular shape. Most apps upload directly to cloud object storage, the files are mostly images and documents, and the most common real failures are not code execution but exposure: files readable by anyone with the URL, buckets listable by the public, or one user able to read another user's uploads.

    How mobile apps usually upload

    PatternHow it worksMain riskVerdict
    Shared storage key in the appApp holds a credential that can write to storageKey extracted, storage abused or readAvoid
    Pre-signed URL from your backendBackend issues a short-lived URL for one objectOverly broad or long-lived URLsGood default
    Authenticated upload endpointApp posts to your API, which stores the fileServer load, size limitsGood for small files and strict checks
    Backend-as-a-service storage with policiesApp uploads with the user's session, policies decide accessPolicy mistakesGood when policies are tested

    The first pattern is still common in AI-built and prototype apps, and it is the one to remove first. Any credential in the app can be extracted, and a storage key that can write can often also read, list or delete.

    Pre-signed URLs done properly

    A pre-signed URL lets the app upload one file directly to storage without holding a storage credential. The backend checks that the user is allowed to upload, decides the object name, and returns a URL valid for a short time. The AWS guide to uploading with pre-signed URLs describes the mechanism, and other providers offer equivalents.

    The details decide whether it is safe:

    • Short expiry. Minutes, not days.
    • Server-chosen object key. A random name in the user's own prefix, never a path the app supplies.
    • Content constraints. Where the provider supports it, restrict content type and maximum size in the signed policy.
    • One object per URL. Never sign a URL that allows writing anywhere in a bucket.

    After the upload, the app tells the backend it is done, and the backend verifies the stored object before treating it as real.

    Backend-as-a-service storage policies

    Apps built on Supabase, Firebase and similar platforms usually upload with the user's session, and access is controlled by storage policies or rules. The Supabase storage access control guide shows how policies restrict which users can read and write which paths. These work well when they are written deliberately and fail badly when a bucket is set public during development and never changed back.

    Test the policies the way an attacker would: sign in as user A, upload a file, then sign in as user B and try to read, overwrite and delete it, and try to list the bucket. The same logic applies as for database rules, covered in checking Supabase RLS policies.

    Validating what was uploaded

    Validation belongs on the server, after the upload, because anything the app checks can be bypassed by sending the request directly.

    Type

    Check the file's actual content, its magic bytes, not just the extension or the content type header the client sent. Accept only the types the feature needs; a profile photo feature needs a few image formats, not PDFs or SVGs. SVG deserves a specific mention because it can contain script, which matters if uploads are ever displayed in a web view or admin panel.

    Size

    Enforce a maximum on the server and, where possible, in the signed upload policy. Unbounded uploads become a cost and availability problem quickly.

    Content

    Re-encode images on the server rather than serving the original bytes. Re-encoding removes malformed structures aimed at image parsers and strips metadata in the same step. For documents that staff or other users will open, scan them with a malware scanner before they become visible.

    Metadata leaks location

    Photos taken on phones often carry EXIF metadata, including GPS coordinates, device model and timestamps. An app that uploads the original image and later shows it to other users can publish a user's home location without anyone noticing. Strip metadata on the server during re-encoding, or on the device before upload, and ideally both, because users increasingly expect it and regulators treat precise location as sensitive.

    Storing and serving files

    Store uploads in a private bucket with random object names, never names derived from user input or user IDs alone. Serve them through short-lived signed download links or an endpoint that checks access on every request. Public URLs that are merely hard to guess are not access control: they get shared, logged, cached and indexed.

    Keep uploads on a separate domain or storage origin from your web app and admin panel, so a file that turns out to be active content cannot run with the privileges of your own site. That is particularly important when staff review uploads in a browser-based admin tool.

    What the app should still do

    Client-side checks are not security, and they are still worth doing for users: limit selectable types in the picker, compress images before upload to save data, show size limits clearly, and resume interrupted uploads. Request only the permissions needed; the system photo picker on both platforms lets users choose individual images without granting access to the whole library. Clear temporary copies of uploaded files from the app's cache afterwards, since sensitive documents left in a temp folder are a common finding, covered in sensitive data in app caches and temp files.

    Upload abuse and cost

    Beyond data exposure, uploads are an easy way to run up costs or degrade a service. A user, or a script using a stolen session, can upload thousands of large files, fill storage and push bandwidth bills up. Limit upload counts and total storage per user, rate-limit the endpoint that issues upload URLs, and delete orphaned uploads that were never confirmed by the app. These limits also make abuse visible early, because a single account hitting them is a signal worth looking at.

    Documents with personal data

    Identity documents, medical forms and financial statements need more than the general controls. Keep them in a separate bucket with stricter access, log every access by staff, set a retention period after which they are deleted, and avoid generating thumbnails or previews that create extra copies. If the app only needs to verify a document once, consider deleting the original after verification and keeping only the result. Less stored data means less to protect and less to disclose if something goes wrong.

    Uploads from AI-built apps

    Prototype and AI-generated apps are where the riskiest upload patterns appear most often. Generated code frequently creates a public bucket to make images display without extra work, uses a service key in the client because it makes the first upload succeed, or skips server-side validation because there is no server code in the project at all. None of this shows up in a demo. Before such an app holds real users' files, review the bucket's visibility, search the code and the built app for storage keys, and move the upload path to signed URLs or policy-controlled storage.

    Downloads deserve the same care

    Upload security is only half the flow. When the app downloads a file to show it, use a short-lived signed link or an authenticated request, and store the downloaded copy in the app's private storage rather than a shared location. On Android, share files with other apps through a FileProvider with temporary grants rather than world-readable paths, as covered in secure file sharing with FileProvider. On both platforms, delete downloaded copies of sensitive documents once the user is done with them, so the device does not quietly accumulate a second archive of everything the user ever opened.

    Logging and monitoring

    Log every upload with the user, file size, detected type and outcome of validation, and alert on spikes in rejected files or unusually large volumes from a single account. Those two signals catch most abuse early. Avoid logging file contents or full signed URLs, since a logged signed URL is a working link to the file until it expires.

    Retention and deletion

    Uploaded files tend to outlive their purpose. Decide how long each kind of upload is kept, delete files when the account is deleted, and remove abandoned uploads that were never attached to anything. Storage that only ever grows becomes a liability: every old identity document or receipt kept without a reason is one more item to protect and one more to report if access controls ever fail. Lifecycle rules in the storage provider can enforce most of this automatically once the retention periods are decided.

    Review the retention rules once a year alongside the privacy policy, so the two say the same thing about how long files are kept.

    Testing with real files

    Test with the files users actually send, not only small sample images: very large photos, HEIC images from iPhones, multi-page PDFs and files with unusual names.

    Checking your upload flow

    Two reviews cover most of it. On the app side, look for storage credentials or bucket names with write access in the built artifact; an automated pass such as PTKD.com flags embedded keys and cloud storage configuration in IPA and APK files, which catches the most damaging pattern quickly. On the backend side, run the two-account test against storage policies, request an uploaded file's URL without a session, try uploading a renamed file of the wrong type and an oversized file, and check whether photo metadata survives.

    Upload security checklist

    1. No storage credentials with write or read access in the app.
    2. Uploads through short-lived pre-signed URLs or authenticated endpoints.
    3. Server-chosen random object names in per-user prefixes.
    4. Server-side type, size and content validation.
    5. Images re-encoded and metadata stripped.
    6. Private storage, access-checked downloads, separate origin.
    7. Scanning for files others will open.

    What to take away

    Uploads are untrusted input from a device you do not control, and the failures that hurt mobile apps most are exposure rather than exploitation: public buckets, guessable URLs, broad credentials in the app and missing access checks. Upload through short-lived pre-signed URLs or authenticated endpoints, validate type, size and content on the server, strip metadata, store privately with random names, and serve through access-checked links. Keep client-side checks for user experience, and test storage policies with two accounts before launch.

    • #file uploads
    • #pre-signed urls
    • #cloud storage
    • #supabase
    • #exif
    • #owasp

    Frequently asked questions

    How should a mobile app upload files securely?
    Through a short-lived pre-signed URL or an authenticated endpoint, never with a storage credential embedded in the app. The backend should choose a random object name, validate type, size and content after upload, store the file privately and serve it only through access-checked links. Client-side checks improve the experience but are not security.
    Are pre-signed URLs safe for mobile uploads?
    Yes, when they are short-lived, limited to a single server-chosen object, and constrained by content type and size where the provider supports it. Risks come from long expiries, URLs that allow writing anywhere in a bucket, and skipping server-side verification of the uploaded object after the app reports success.
    Why should I strip metadata from uploaded photos?
    Phone photos often carry EXIF data including GPS coordinates, timestamps and device details. If uploads are shown to other users, that metadata can reveal where someone lives or works. Re-encoding images on the server removes metadata and malformed structures in one step, and stripping on the device as well adds a second layer.
    Is checking the file type in the app enough?
    No. Anything the app checks can be bypassed by sending the upload request directly. Check the actual file content on the server, its magic bytes, against an allow list of the types the feature needs. Keep the app-side check as well, because it gives users fast feedback and avoids wasted uploads.
    When is a public storage bucket acceptable?
    Only for content that is genuinely public by design, such as marketing images or public product photos, and never for user uploads. A public URL that is merely hard to guess is not access control, because links get shared, logged and indexed. User files belong in private storage with access-checked downloads.

    Keep reading

    Scan your app in minutes

    Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.

    Try PTKD free