A polyglot file is a single file that is simultaneously valid in two or more formats. It can satisfy an image parser and a script interpreter at the same time, which lets it pass validation designed to accept one format while behaving as the other when something else opens it.
Why a file can be two things
It sounds like it should be impossible, and it stops sounding impossible as soon as you look at how formats are actually defined.
Most formats identify themselves with a short signature near the start of the file, and then describe how to interpret what follows. Most of them are also relaxed about material they do not recognise. An image format will happily ignore bytes after the point where the image data ends, because tolerating unexpected trailing content is how formats survive decades of tooling written by different people.
Interpreted languages tend to be relaxed in a different way. Rather than requiring their content at a fixed position, they scan for their own opening marker and act on what sits between it and the closing one. Everything outside is not their concern.
Put those two tolerances together and you have the polyglot. The beginning of the file satisfies the image parser, and a region further in satisfies the other parser. Both are behaving exactly as specified. There is no bug being exploited in either one, which is precisely what makes this difficult to defend against by detection.
The validation ladder, and where it stops
Applications tend to arrive at file validation in stages, each one prompted by discovering the last was insufficient. The ladder is worth setting out, because it shows exactly where polyglots sit.
- Check the extension. Trivially bypassed, since the user chooses the filename
- Check the Content-Type header. Also chosen by the client. PortSwigger's Web Security Academy is blunt that where this header is implicitly trusted, the defence can be bypassed with an intercepting proxy
- Check the file's leading bytes. A real improvement, and PortSwigger calls content validation "a much more robust way of validating the file type". This is the rung most careful applications reach
- The polyglot arrives. Its leading bytes are correct, because satisfying that check is what it was built to do
PortSwigger puts the ceiling plainly: using tools such as ExifTool, it is trivial to create a polyglot JPEG containing malicious code within its metadata. The image is a real image. Open it and you see a picture.
This is why checking magic bytes rather than extensions is necessary but not sufficient. It closes the easy bypasses and leaves this one open, and no amount of refining the check closes it, because there is nothing invalid to find.
What actually works: rebuild rather than inspect
The productive move is to stop asking whether the file is safe and start ensuring that what you keep is only the part you wanted.
For images this is re-encoding, and it is both cheap and effective. Decode the uploaded file into raw pixels, then write an entirely new file from those pixels. The output contains image data and nothing else. Any second format that was riding along in the metadata or the trailing bytes is simply not carried across, because it was never part of the pixel data you decoded. You do not have to detect anything.
Content disarm and reconstruction generalises that idea to documents. A PDF or Office file is taken apart, the components that carry legitimate content are identified, and a new document is assembled from those alone. Structures that could carry executable behaviour are not evaluated and rejected; they are simply not included in the rebuild.
The reason this suits polyglots specifically is that it inverts the question. Detection asks "is there something bad in here?", which requires knowing what bad looks like. Reconstruction asks "what is definitely legitimate content?", and discards the rest by default. A file format nobody has thought of yet still does not survive being rebuilt.
It is worth being straightforward about the cost. Re-encoding and reconstruction both change the file. Metadata goes, and for images that includes orientation and colour profile information you may need to carry across deliberately. If your users depend on something inside the file that you are discarding, you have to handle that on purpose. That is a real trade, and it is a better one than it looks, because the alternative is trusting a check that has a known ceiling.
The controls to pair it with
Rebuilding removes the ambiguity in the file. These reduce what a polyglot could reach if one ever survives, and PortSwigger's own list, which lines up with the OWASP File Upload Cheat Sheet, is a sound baseline:
- Check extensions against a list of permitted types rather than a list of prohibited ones
- Make sure the filename cannot be read as a directory or traversal sequence
- Rename uploaded files, so nothing can overwrite anything that already exists
- Keep uploads off the permanent filesystem until they have been fully validated
- Use an established framework for handling uploads rather than writing the validation yourself
To that, add serving user content from a separate origin, and never storing uploads anywhere the web server will execute them. A polyglot that reaches disk but can never be requested as a script has lost its most direct route to becoming a web shell.
You cannot reliably spot a polyglot. You can make sure nothing survives the rebuild. The Red Eagle Tech CDR API reconstructs documents from their legitimate components and returns a file your users can still open. Published per-document pricing, OAuth2, and you can be running in a few minutes entirely self-service.