What Is a Polyglot File and Why Upload Checks Miss It
A polyglot file is valid in two formats at once, such as an image that also holds script. Learn why upload checks miss it and how to defend.
A polyglot file is a single file that is valid in two formats at once, for example an image that also contains text a script interpreter will accept. Upload filters that only check whether a file looks like an image will pass it. If the server later hands that file to an interpreter, or a browser renders it as HTML, the hidden content runs. The 2026 Magento PolyShell attacks relied on uploads disguised as images, which is why understanding polyglot files matters for anyone running an upload feature.
What is a polyglot file?
A polyglot file is a file whose bytes satisfy the rules of more than one file format, so different programs read it as different things. An image viewer sees a picture, while a PHP interpreter or a browser can see code in the same file.
Most file formats are forgiving. Image decoders read the header and the image data they need and ignore the rest. Interpreters such as PHP skip everything outside their own opening and closing tags. Browsers that sniff content may treat a file as HTML if it contains markup, whatever the extension says. Each program applies its own rules, and a polyglot file is built so that each set of rules is satisfied.
The term covers harmless uses too. Some format test suites and file archives use polyglots deliberately. In security, the concern is narrow: a file that passes one check because it is one thing, then gets executed because it is also another.
Why do upload checks miss polyglot files?
Upload checks miss polyglot files because most of them confirm what a file is, not what else it could be. A check that reads the image header, confirms the dimensions or matches a MIME type will pass a file that is a real image with something extra attached.
Common checks and their blind spots:
- Extension allowlists. Rejecting anything that is not
.jpgor.pnghelps, but only if the server never runs those extensions and the filename cannot be manipulated, for example with odd characters that change how the name is parsed. Sansec noted some PolyShell file names used Unicode escapes to disguise their extensions. - MIME type from the client. The browser or attacker supplies the
Content-Typeheader. It proves nothing. - Magic bytes. Checking the first bytes of the file confirms the format the file claims. A polyglot starts with those bytes on purpose.
- Image library parsing. Opening the file with an image library confirms it decodes. A polyglot decodes cleanly.
None of these checks asks the question that matters: what will the server and the browser do with this file once it is stored?
How polyglot uploads turn into incidents
A polyglot on disk is inert until something interprets it. The dangerous step is almost always a server or browser configuration, not the upload itself.
Server-side execution. If the web server passes files in an upload directory to PHP, a file with a .php name, or one the server is configured to treat as script, runs with the web server's permissions. That is how an upload becomes a web shell. In the PolyShell case, Sansec found that remote code execution depended on the web server setup: nginx configurations that pass every .php request to FastCGI, and Apache without PHP execution disabled, were the risky ones. The fix for that layer is described in how to block PHP execution in Magento media folders.
Browser-side execution. If an uploaded file is served from the same domain as the application and the browser renders it as HTML, any script in it runs with the site's origin. That is stored cross-site scripting. Sansec reported stored XSS risk for PolyShell on all stores in some configurations, even where PHP could not run.
Persistence. Files stay on disk after the vulnerability is patched. A later configuration change, such as a new nginx include or a migrated server, can make a dormant file executable. This is why incident response after an upload flaw always includes an inventory of what is already in the upload folders, as the Adobe Commerce PolyShell CVE-2026-48356 advisory recommends.
How do you defend an upload feature against polyglot files?
You defend an upload feature by controlling where files are stored and how they are served, so that no stored file can ever be executed or rendered as active content. Content checks help, but storage and serving rules do the real work.
Practical controls, roughly in order of value:
- Store uploads outside the web root, or on a separate domain. If uploads must be served, serve them from a domain with no cookies or session context, so a rendered HTML file cannot reach the application's origin.
- Never execute anything in upload directories. Deny script handlers for those paths at the web server, and deny direct access to directories that only the application should read.
- Rename files on the server. Generate the stored name and extension yourself. Do not keep the user's filename.
- Re-encode images. Decoding the image and writing a fresh copy drops anything the format did not need. It costs some CPU, and it is one of the few checks that removes appended content instead of detecting it.
- Send safe headers. Serve user files with an accurate content type, tell browsers not to guess a different one, and force a download for anything that does not need to display inline.
- Monitor the directories. File integrity monitoring on upload paths turns a quiet compromise into an alert. A script file appearing in an image folder should page someone.
Where polyglot risk shows up beyond Magento
Any application that accepts files from untrusted users has this exposure: CMS media libraries, support portals, profile picture uploads, document management systems and e-commerce product options. Self-hosted platforms carry more risk because the server configuration is your responsibility, and one permissive rule written years ago can undo every content check.
Patching the application closes a specific upload path. It does not change what your web server will do with files that are already stored. Keeping upload handling locked down at the server layer is what keeps the next upload bug from becoming code execution. For the patch side of the problem, see why Magento isolated patches need the latest patch level.