Four ways an image file can attack a server, and what stops each
A file is not what its name says it is. Here is what is checked, in what order, and why byte limits catch none of it.
4 min read
The filename and the content type are both attacker-controlled
Nothing about an upload can be taken on trust. The extension is a suggestion, the declared content type is whatever the client felt like sending, and the length header can be a lie. So the only thing worth believing is the bytes.
The opening chunk of every upload is read and the real format identified from it, before a single byte is written to storage. The file is then stored under a name built from generated identifiers, and the extension comes from what the bytes turned out to be — never from what the file was called.
A decompression bomb is small on disk and enormous in memory
Compression means the size of a file says nothing about the cost of opening it. A test file in this project is 593 KB and expands to roughly 1.9 gigabytes once decoded — comfortably inside every byte limit the service has, and fatal to whatever tries to open it.
Byte caps are therefore the wrong instrument entirely. But the dimensions are written in the file header, ahead of any of the compressed data, so they are read and multiplied first. Anything declaring more than one hundred megapixels is refused before a decoder ever sees it.
A file can honestly be two formats at once
A ZIP reader does not start at the beginning of a file. It seeks to the end and works backwards to the archive directory. That means an archive appended to a JPEG produces one file that genuinely is both: valid image at the front, valid archive from the back.
A check that only reads the opening bytes passes it every time. So the end of the upload is watched too — in a small fixed window, so a fifty megabyte file costs no more memory than a small one — looking the same way a ZIP reader looks. The record it finds is validated structurally as well, because the signature alone would occasionally appear by chance in ordinary compressed photographs.
SVG is the format that is also a program
An SVG can carry script, event handlers, references that fetch remote addresses when viewed, and embedded HTML. Served from a domain, that is a cross-site scripting hole rather than a picture.
The approach here is sanitise, then verify. The active parts are stripped, and the result is then re-inspected by a deliberately over-eager detector; if anything still looks dangerous the file is refused rather than served. That trade is the point — being wrong costs a rejected upload, never an executed script.
- Filenames
- Kept only as a label. Storage keys are built from generated identifiers, so a name containing path segments cannot reach the filesystem.
- Archive entries
- Names in a ZIP download are flattened, so an entry cannot escape the folder it is extracted into.
- Command-line tools
- Invoked with argument arrays, never assembled into a shell string.
Frequently asked questions
- My file was refused and I do not think it is dangerous. What happened?
- Most likely it declared more pixels than the cap allows, or it is an image with something appended to it. Re-saving it from an image editor and uploading that usually resolves it.
- Can I still upload SVGs?
- Yes, for the tools that accept them. What comes back is the cleaned version. A file that cannot be cleaned safely is refused with the reason.
- Are these checks on the API too?
- Yes, and not as a copy. The website and the public API call the same upload code, so neither can end up enforcing an older set of rules than the other.