Case study: hardening upload validation when extensions lie
Incident
Internal admins could attach “screenshots” to tickets. A contractor uploaded invoice.jpg that opened fine in Preview on macOS but crashed a server-side thumbnail worker. Logs showed the file was mostly ASCII text with a .jpg extension — no JPEG SOI marker (FF D8).
Initial response was blunt: block anything where extension ≠ sniffed MIME. That broke legitimate HEIC→JPEG exports from certain mobile tools where metadata confused older sniffers.
Testing approach
We built a small matrix of synthetic files (all generated locally):
- Valid minimal JPEG with correct magic bytes.
- Plain text padded to 200 KB renamed to .jpg.
- PDF header with .png extension (polyglot-style probe, used only in staging).
Each file was run through the upload API, the async virus scan hook, the thumbnail queue, and the CDN origin fetch. We recorded pass/fail per stage — not just the HTTP status of the initial POST.
Layered defenses (what actually shipped)
- Client hint only: browser-reported MIME is never trusted for security.
- Magic-byte gate for images: require SOI for JPEG, PNG signature for PNG, before enqueueing thumbnails.
- Quarantine bucket: mismatches land in a non-public prefix; support gets a ticket ID, not a stack trace.
- Human override: admins can re-classify after manual review — audited.
False positive lesson
Some Android camera apps produced JPEGs with uncommon APP segments. Strict “first 16 bytes must match a textbook table” rejected them. We moved to a two-step check: SOI present + decode attempt with a sandboxed library timeout.
Try it yourself
Use the magic-byte sniffer to inspect headers without uploading secrets, then walk through the extension vs sniffed MIME scenario with your staging credentials only.
