All resources
// Resources

Secure File Upload Review Checklist

Published June 22, 2026

File upload is a pipeline, not a form field. Filename, MIME header, and extension are attacker input. OWASP’s File Upload Cheat Sheet and CWE-434 recommend layered handling.

Map each stage

Trace receive, type inspection, storage, scanning, transformation, retrieval, and deletion. Example: invoice.pdf.php must not become executable because web server maps its last extension. Example: an SVG can contain active content even when its declared MIME type is image.

Upload control matrix

StageControlTest case
acceptbusiness allowlist and size cappolyglot rejected
storegenerated name outside web rootdirect path cannot execute
processisolated parser and timeoutmalformed archive cannot exhaust worker
servedisposition and access checkprivate file URL denied

Decision rule: if application cannot safely parse or serve a format, do not accept it.

Remediation sequence

Allow only needed formats, inspect magic bytes, store outside executable paths, scan asynchronously, and authorize every download. Antivirus results do not prove a document harmless; keep parser isolation. Code review can inspect storage and retrieval paths.

Treat processing as untrusted execution

Validate business need before accepting a format. Decode filename before extension checks, use a narrow allowlist, inspect type signatures, and assign a server-generated identifier. Content-Type is useful telemetry but never authority. Store objects outside executable web paths and map retrieval through authorization rather than exposing filesystem names. A scanner result can add signal; it cannot make a parser safe.

Put conversion, preview, and archive extraction in a constrained worker with CPU, memory, time, and output-size limits. Reject or quarantine when inspection is inconclusive. Download paths should recheck ownership and choose Content-Disposition deliberately. Test an oversized archive, mismatched signature, double extension, traversal-like filename, active content, and unauthorized retrieval. Owner: upload service owner. Pass: rejected samples never execute, persist as available content, or reach another user. Fail: any sample bypasses a stage. Exceptions record format, reason, isolation, approver, remediation, and expiry.

Storage and retrieval contract

Return an application object ID, never a storage path. Associate object with owner, authorization scope, detected format, scan state, retention rule, and processing result. Keep pending objects unavailable. If preview is needed, use a separate origin or isolated service rather than inline original bytes. Demonstrate guessed identifiers cannot bypass authorization.

Upload chain receipt

Expected output: upload object ID, claimed MIME type, detected signature, byte size, quarantine state, converter exit code, retrieval authorization result. This pipeline record identifies exact stage that contained dangerous content.

Worked failure: A PDF-named file contains an executable signature. The intake service must quarantine it and must not invoke preview conversion or publish a download link.

Edge case: An authorized upload can become unauthorized after account membership changes. Retrieval must re-check present access instead of trusting upload-time permission.

Closure test: Attempt download with a guessed object ID after scan completion. Closure passes when storage name stays hidden and authorization denies the request.

Parser containment drill

Choose a deliberately malformed sample for every accepted format. The test must show which worker receives it, how long processing runs, how output is bounded, and where failure state is stored. Archive handling needs separate accounting for entry count, expanded byte count, and nested archives; a compressed file below request limit can still exhaust a converter. Do not extract to a path derived from the upload name. Generated temporary paths need cleanup on timeout and worker restart.

Retrieval tests cover more than response headers. Verify an object is unavailable while scanning, deleted objects cannot be resurrected from derivative cache, and a share decision is enforced at every download. When public distribution is required, use a separate policy for abuse reports and content removal rather than giving private uploads public URLs by default. A remediation change closes only after an old malicious fixture and a normal accepted fixture both pass expected pipeline states.

Operational output must identify whether rejection occurred at intake, scan, conversion, storage, or retrieval. A failed conversion must leave no public derivative. Re-run same accepted file after worker restart to prove cleanup does not delete approved content or expose quarantined bytes.

File type decisions should be versioned with product requirements. When a new format is requested, assess parser history, active-content behavior, preview need, retention, and public distribution before adding it to allowlist. A format accepted only for archival storage may have different controls from a format rendered in browser. Require a new malicious fixture and normal fixture before rollout. This keeps an expanding accept list from becoming an unreviewed compatibility promise.

Sources

Have a system that needs testing?