File Upload Security QA: Type, Size, Storage, Scanning, Cleanup
File upload security QA verifies every stage through which untrusted bytes pass: request admission, authorization, filename handling, extension and content validation, size.

File upload security QA verifies every stage through which untrusted bytes pass: request admission, authorization, filename handling, extension and content validation, size enforcement, quarantine, scanning, permanent storage, public delivery, retention, and deletion. No single check proves a file safe, so the release gate uses independent controls and tests their failure behavior.
Run this workflow in an isolated test environment with harmless fixtures designed to exercise parsing and policy. Do not create, upload, or distribute real malware. Keep credentials, private documents, production records, and personal data out of test files, screenshots, logs, and issue trackers.
Map The Complete File Lifecycle
Draw the path from browser or API client through CDN, proxy, application, temporary directory, scanner, object store, database, transformation worker, and download route. Mark every trust boundary, file copy, metadata record, queue, cache, and deletion owner. Security review cannot stop at the form handler.
Record which component enforces authentication, authorization, request size, accepted formats, filename generation, scanning, encryption, retention, and delivery headers. Identify what happens when any dependency is unavailable. An undocumented fallback often becomes the path that bypasses the intended control.
Define An Upload Contract
For each upload purpose, specify who can upload, the business reason, maximum files, per-file and aggregate limits, accepted media types, required dimensions or structure, retention, visibility, transformations, and consumers. A profile image and a private contract need different policies even if both arrive as multipart data.
Use an allowlist that is as narrow as the feature permits. OWASP recommends allowlisted extensions rather than trying to enumerate every dangerous type. Treat the contract as versioned input policy and require approval before expanding it.
Authenticate And Authorize
Require a valid authenticated session or scoped API credential where the feature is not intentionally anonymous. Then authorize the specific action and owning record. A user who can upload an avatar must not attach files to another account by changing an identifier.
Test expired sessions, revoked roles, missing ownership, cross-tenant identifiers, hidden form fields, direct API calls, and stale signed URLs. Deny before accepting a large body where infrastructure permits, and return a stable error that does not reveal storage paths or policy internals.
Protect The Request
Apply CSRF protection to browser workflows that rely on ambient cookies, as OWASP advises. Validate origin and session behavior according to the application’s architecture. For APIs, bind credentials and scopes to the intended endpoint and reject unexpected methods and content types.
Test duplicate submissions, interrupted uploads, chunk reordering, replayed completion calls, and concurrent finalization. Assign one upload identity and make completion idempotent so retries do not create multiple stored objects or metadata rows.
Enforce Limits At Every Layer
Set aligned limits at CDN, reverse proxy, web server, framework parser, application, chunk protocol, decompressor, image processor, and permanent storage. Check compressed and expanded size, image dimensions, page count, archive entries, processing time, and total quota as applicable.
Test just below, exactly at, and just above every boundary. Include slow bodies, missing lengths, chunked transfer, many tiny files, highly compressed input, and concurrent uploads. Reject early and clean temporary bytes even when the client disconnects.
Generate Server-Side Names
Ignore client paths and do not use the supplied filename as a storage key. Generate a high-entropy identifier and store the original display name only as sanitized metadata when the product needs it. OWASP recommends application-generated filenames and restricted character handling.
Test traversal separators, drive prefixes, reserved device names, control characters, Unicode normalization, bidirectional text, leading dots, trailing spaces, repeated extensions, and names that collide after normalization. Storage keys must remain inside the intended namespace under every parser.
Validate Extensions
Normalize case and decode the filename once according to the request framework, then compare the final extension with the feature allowlist. Reject double extensions, trailing separators, alternate data syntax, null-like terminators, and parser discrepancies. Do not infer safety from a friendly suffix.
Keep extension validation separate from content validation. A JPEG name on an executable body should fail, while a valid image with a misleading name should not be silently reclassified unless the product explicitly supports controlled normalization.
Treat Declared MIME As A Hint
The client-supplied Content-Type can help produce an early error, but it is attacker-controlled. Compare it with the allowlist and then determine the content independently using trusted parsers, signatures, and actual decoding. Record mismatches as security-relevant evidence.
Do not accept a file solely because a generic sniffing library reports a common type. Polyglot files and malformed containers can satisfy shallow signatures. Validate the exact structure and required properties for the business feature.
Inspect Signatures And Structure
Check expected magic bytes where they are meaningful, then fully parse with a maintained library configured for bounded resources. Verify dimensions, channels, pages, archive entries, required fields, and end-of-file behavior. Reject truncated, malformed, encrypted, or unsupported variants according to policy.
Use isolated parsing workers with memory, CPU, time, recursion, and output limits. A parser crash or timeout is a rejection, not permission to store publicly. Capture the policy reason without logging raw untrusted content.
Handle Archives Carefully
Avoid archives when the feature does not require them. When they are necessary, limit entry count, total expanded size, compression ratio, nesting, path depth, and per-entry type. Reject absolute paths, traversal, links, special files, duplicate normalized names, and encrypted members that cannot be inspected.
Extract only into a fresh isolated directory and never over application or shared storage. Validate every member after extraction and clean the entire workspace on success, rejection, timeout, and worker termination.
Quarantine Before Trust
Write new objects to nonpublic quarantine storage with generated keys, restrictive permissions, and a pending state. Do not return a public URL or allow downstream processing until validation and scanning complete. Bind the metadata row, object hash, size, uploader, policy version, and scan result.
Make state transitions atomic. A worker retry must not promote an older object or a file belonging to another record. Test queue duplication, delayed scans, database rollback, object-store timeout, and application restart.
Scan Without Overclaiming
Use antivirus, sandboxing, or content disarm and reconstruction when appropriate and available, as OWASP suggests. Version scanner engines and signatures, define timeout and unavailable behavior, and preserve a minimal audit record. A clean result means no configured detector fired; it does not prove harmlessness.
Fail closed for features whose policy requires scanning. Decide how to rescan retained files after signature updates or incidents. Do not expose scanner names, signatures, or detailed detection paths to untrusted users when that information would aid evasion.
Store Outside Executable Paths
Prefer a separate host or object store outside the web root. Apply least-privilege service identities, encryption, tenant boundaries, retention controls, and private defaults. MITRE CWE-434 highlights the danger of files that can be processed as executable or otherwise unsafe content.
If local storage is unavoidable, prevent execution, script interpretation, and direct path access. Verify deployment images, backups, replicas, and disaster-recovery copies follow the same policy. A secure primary bucket does not compensate for a public backup.
Serve Through A Controlled Route
Map an opaque identifier to the authorized object after checking access. Set a deliberate Content-Type, Content-Disposition, X-Content-Type-Options, caching policy, and range behavior. Use attachment delivery for content that must not execute in a browser context.
Test inline SVG, HTML, PDF scripting, active office documents, mismatched MIME, cached private responses, predictable identifiers, and expired signed links. Never reflect unsanitized filenames into headers; encode display names using a supported standard routine.
Re-Encode Media When Appropriate
For accepted images or media, decode and re-encode through a maintained library when product requirements permit. Strip unneeded metadata, enforce orientation and color handling, and generate controlled derivatives. Preserve originals only when the business requirement and security policy explicitly call for them.
Re-encoding reduces some hidden content and malformed structure but is not a universal sanitizer. Test animation, transparency, extreme dimensions, profiles, metadata, and decoder limits. Keep derivative generation in isolated bounded workers.
Log Events Without Leaking Files
Record upload identity, account, owning record, time, policy version, normalized type, sizes, object hash, state transitions, scanner result category, and deletion outcome. Avoid raw file bodies, secrets, access URLs, private filenames, or personal document text in general logs.
Alert on repeated policy failures, unusual volume, many tenants targeted by one account, scanner outages, processing spikes, and public-access changes. Correlate browser, API, worker, and storage events through one safe identifier.
Test Cleanup And Retention
Delete temporary chunks, quarantine objects, failed derivatives, database metadata, caches, and permanent objects according to policy. Cover user cancellation, rejected scans, abandoned sessions, replaced files, deleted parent records, account deletion, retention expiry, and legal holds.
Run reconciliation jobs that find metadata without objects and objects without metadata. Test partial deletion and retry idempotently. Verify backups and replicas have documented expiration rather than claiming immediate erasure that the architecture cannot provide.
Release With Evidence
Build a matrix of accepted and rejected fixtures, expected status, storage state, public reachability, logs, cleanup, and owner. Block authorization bypass, executable delivery, public quarantine, limit failure, path escape, scanner bypass, sensitive logging, or orphaned files.
Connect these checks to broader API input validation and trust-boundary verification. The Full Stack Web Development course can strengthen the browser, API, storage, testing, and deployment skills behind the workflow.
FAQ
Is checking the file extension enough?
No. Use an allowlisted extension together with content, signature, structural, authorization, limit, storage, and delivery controls.
Does an antivirus clean result prove a file is safe?
No. It means the configured scanner found no known detection; independent controls and cautious serving remain necessary.
Where should uploaded files be stored?
Prefer private storage outside executable web paths, with generated keys and controlled authorized delivery.
Want to Build Practical Technology Skills?
Explore RisingEdge courses designed to help students learn real skills, build projects, and prepare for career opportunities.



