I'm building an ecommerce chat widget where users can upload product photos. I only want to accept genuine JPEG, PNG, or WebP files and reject anything that merely disguises another file type as an image.
My current plan is to perform two checks: first, inspect the client-provided Content-Type header and reject requests that are not marked as images; second, inspect the file contents in memory with python-magic to determine its actual MIME type. The files will eventually be stored in Cloudflare R2, and I'd also like to enforce a reasonable size limit so invalid or oversized uploads don't remain in storage.
Is this a sound approach, or should I validate the files differently?
4 Answers
Use an image parser such as Pillow to validate that the file is structurally readable, not just that its first bytes resemble an image. For example, open the upload with Pillow and call its verification method, then reopen it if you need to process or convert it. You can also decode and re-encode accepted images to a safe output format, which strips away unexpected metadata and embedded content. Keep the allowed formats explicit: JPEG, PNG, and WebP.
Set a strict maximum request and file size before accepting the upload. Ideally, reject the request as soon as the declared or received size exceeds the limit instead of letting the entire body arrive first. Also consider image-dimension limits, since a small compressed image can expand to consume a large amount of memory when decoded.
A layered check is the right general design: enforce the size limit, treat the header as an untrusted hint, identify the file from its contents, and then have a trusted image library decode it. Store uploads using generated names rather than user-provided filenames, keep them outside any executable path, and only send validated files to object storage. Content validation reduces risk, but it shouldn’t be treated as a complete malware scanner.
Treat the request’s Content-Type as an initial filter only—it’s supplied by the client and can be forged. After that, restrict uploads to the formats you explicitly support and inspect the file’s contents to verify that they match the claimed type. Reject anything that fails validation with a 400-level response rather than storing it. Avoid accepting formats such as SVG unless you have a specific, carefully sandboxed reason to support them, since they can contain active content.

Exactly. The important distinction is that the header is useful for early filtering, but it should never be the security decision by itself. The file signature and a real decoder need to agree.