XML External Entity injection is easy to miss in review because the vulnerable line is almost always the XML parser's default configuration, not application code someone wrote deliberately. A codebase can parse XML safely for years and then become exploitable the moment a new endpoint accepts XML from an untrusted source using the same parser everything else already uses.
What makes a parser exploitable
The XML spec allows a document to define custom entities, and by default many XML parsers will resolve an external entity that points at a file path or a URL — <!ENTITY xxe SYSTEM "file:///etc/passwd">— and substitute its contents wherever that entity is referenced in the document. An attacker who can submit XML that gets parsed with this default left on can read local files, reach internal network services the application server can access (an SSRF pivot), or, in the worst case, trigger a denial-of-service through an entity that references itself recursively (a "billion laughs" expansion).
Where XML parsing shows up unexpectedly
- SOAP APIs and any legacy web service integration still communicating in XML.
- SAML authentication — SAML assertions are XML documents, and identity provider integrations are a classic XXE target.
- File format parsers that don't look XML-related on the surface: DOCX, XLSX, PPTX, and SVG are all XML under the hood, so an "upload your profile picture" feature that accepts SVG is an XML parsing endpoint.
- RSS/Atom feed parsers, sitemap importers, and any "paste your XML config" admin feature.
- Older REST APIs that accept both JSON and XML request bodies based on
Content-Type.
The fix is almost always a parser flag, not custom code
Every major XML library has a setting to disable external entity resolution and DTD processing entirely, and for the vast majority of applications there is no legitimate reason to have it on — most systems parsing untrusted XML don't need external entities at all. Java's DocumentBuilderFactory needs setFeature("http://apache.org/xml/features/disallow-doctype-decl", true), .NET's XmlReaderSettings needs DtdProcessing.Prohibit, Python's lxml needs resolve_entities=False on the parser, and PHP's libxml needs LIBXML_NOENT left off with entity loading disabled via libxml_disable_entity_loader. The exact call differs by library, but the review question is always the same one: does this parser have external entity resolution turned off, explicitly, at the point it's constructed.
Why this keeps recurring in newer languages too
It's tempting to treat XXE as a legacy-Java problem, but any language whose XML library ships with permissive defaults for backward compatibility reintroduces the same bug. The review habit that holds up across ecosystems is checking every new XML parser instantiation for its entity-handling configuration, the same way a reviewer checks every new database query for parameterization — not assuming a modern stack made the problem go away.
Checklist for reviewing a diff that parses XML
- Every XML parser instantiation explicitly disables DTD processing and external entity resolution, rather than relying on library defaults.
- File-upload features accepting XML-based formats (SVG, DOCX, XLSX, RSS) route through the same hardened parser configuration as any other XML endpoint.
- SAML and SOAP integrations use a library or configuration known to reject external entities by default, since these are the highest-value XXE targets in most stacks.
- A parser change or library upgrade doesn't silently reset an entity-handling flag that was set explicitly elsewhere in the codebase.
See how to spot SSRF in code review →
Read how to detect insecure deserialization in code review →