OWASP Top 10Long read

XML External Entity Injection in Document Processing APIs

Hidden XML parsers in APIs inherit permissive defaults that enable file theft and credential access.

· 11 min read
Cover illustration for “XML External Entity Injection in Document Processing APIs”
OWASP Top 10 · October 10, 2026 · 11 min read · 2,559 words

XML external entity injection, or XXE, exists because XML parsers were built to do what attackers later learned to abuse. The Document Type Definition system gives a document the ability to pull in outside content by reference, and that was the design goal from the start, not an oversight someone forgot to patch.

You can explain how an XXE attack works in one breath. A DTD block at the top of an XML document declares an external entity that points to a file path on the local system. The document body then references that entity by name. When the parser processes the file, it fetches whatever the entity points to and drops the contents directly into the parsed document, and the application layer never gets a look at the data first. No password is checked. No application logic is bypassed or tricked. The parser is doing its job, following a specification that predates most of the web frameworks built on top of it. That is the structural root of XXE: a trust decision made decades ago, still sitting active in default parser configurations today.

Where XML Lives in a Modern Document Processing API Stack

Teams building REST and JSON APIs often assume XML is someone else's problem. That assumption is wrong often enough to matter, because XML is embedded inside protocols and file formats that don't advertise themselves as XML.

SAML is the clearest case. Any service that hands off authentication to a SAML identity provider is parsing XML to process assertions and signatures, no matter what format the rest of the API uses for everything else. Office documents carry the same hidden surface:.docx,.xlsx, and.pptx files are ZIP archives with XML inside them, so a file upload endpoint that accepts these formats is functioning as an XML parser whether the engineering team built it with that in mind or not. SVG images are XML by definition, so any endpoint that accepts SVG uploads or renders user-supplied SVGs carries the same exposure. SOAP, meanwhile, keeps running inside enterprise B2B integrations and internal systems that have been operating quietly for years, and if a modern application bridges into one of those legacy backends, it can inherit a SOAP surface it never directly built or tested.

What makes this worse is how parser configuration travels. When XML parsing runs through shared libraries or middleware, the permissive defaults set in one component spread automatically to every service downstream of it, often without anyone deciding that should happen. Invicti's research on SAML implementations points to this directly: XXE appears in authentication and identity paths because parser settings get inherited across identity services, gateways, and the applications sitting behind them. OWASP's own Top 10 reflects this reality. XXE moved into the Security Misconfiguration category in 2021, as A05, and holds a position in the 2025 list, at A02, the first major revision of the Top 10 since 2021. The move names where the root cause lives: in configuration, not in code.

The four attack chains that a permissive parser enables

A permissive XML parser isn't a narrow bug that leaks a file here or there. It opens the door to four distinct attack chains, and each one carries a different blast radius and a different level of difficulty to detect.

The first is direct file disclosure. The parser fetches a local file path and injects its contents into the document being parsed, and if the application reflects that content back in a response, a log, or an error message, the attacker reads it. The usual targets are predictable: /etc/passwd, application.env files, database configuration files, /proc/self/environ, private SSH keys, SSL certificates. CVE-2025-49493, found in Akamai CloudTest, shows the pattern in practice. A SOAP endpoint accepted POST requests and parsed the XML body. When you probe it with an external entity pointing to /proc/self/environ, the parser returned an error that included the file path as the system-id, so external entity resolution was happening before any data had been cleanly extracted. After that point, error-based and out-of-band techniques pulled the contents of /etc/passwd out.

The second and third chains run together as one escalation path. Instead of pointing at a local file, the entity points at an internal HTTP URL, and the parser makes that request from inside the infrastructure and hands the response back. The AWS instance metadata endpoint is reachable only from within the EC2 instance itself. An XXE-vulnerable parser running on that instance can fetch it on the attacker's behalf. Other reachable targets include internal admin interfaces, Spring Boot actuator endpoints, internal Elasticsearch clusters, and service discovery systems. Invicti describes this as SSRF into services never meant to be reached from the internet: the server itself becomes a proxy into the private network. The danger sharpens considerably once that SSRF is aimed at the cloud metadata service specifically, because the response contains short-lived IAM credentials. Those credentials open the door to lateral movement across an entire cloud account, so a file-read bug becomes a credential theft incident and the attacker never needs to escalate privilege. The metadata service simply hands the keys over.

The fourth chain is denial of service through recursive entity expansion, with the Billion Laughs pattern as the textbook example: nested entity references expand exponentially until parser memory runs out. This chain leaves no data in the attacker's hands, unlike the first three. It also requires no data reflection to succeed, which makes it the easiest of the four to trigger, even though it's the least strategically interesting of the group for an attacker focused on data or credentials.

SAML endpoints as the highest-risk XML surface in SaaS authentication flows

CVE-2025-34142, found in ETQ Reliance, traces to the /resources/sessions/sso endpoint, where the SAML authentication handler processed XML input without disabling external entity resolution. Crafted SAML responses could invoke external entity references, opening the door to file retrieval or SSRF. CVE-2024-52596, found in SimpleSAMLphp, allowed unauthorized users to read configuration files and access the private keys used to sign SAML assertions, which could lead to unauthorized disclosure of sensitive files, SSRF, and denial of service.

Both cases point to the same structural fact: SAML is the authentication layer, so a compromise there doesn't just expose data, it can undermine the identity system every downstream service depends on. If an attacker gets hold of a signing key used for SAML assertions, they can forge assertions that look legitimate to every service trusting that identity provider. The handler receiving the SAML response looks trusted and authenticated by design, and it has to receive XML because the specification requires it. The parser underneath it often goes unhardened because the XML is assumed to arrive from a known, trusted identity provider, not from an attacker.

The common defense against this concern is some version of "our SAML library handles it." That defense misses where the risk actually sits. The underlying XML parser that the SAML library delegates to, along with whatever shared parser configuration got inherited rather than explicitly set, is where the problem actually sits. Invicti's research makes the same point: XXE turns up in authentication paths because parser settings are easy to inherit and hard to standardize across identity services, gateways, and the applications sitting downstream of them.

Third-party libraries and XXE risk in code you never wrote

A team can harden every XML parser it directly controls and still carry XXE risk, because that risk lives inside every third-party library that parses XML on the application's behalf. Those libraries run their own parser instances, and an application's hardening settings don't automatically reach inside them.

Apache Tika's CVE-2025-66516 shows how far this extends. Rated 9.8 Critical by NVD and NIST, and 8.4 High by the Apache CNA, the flaw sits in how Tika processes PDF files containing XFA (XML Forms Architecture) data. The vulnerability lives in tika-core itself, not just the PDF module, so any system running an affected tika-core version is exposed no matter how carefully the surrounding application code was written. Tika sits embedded across a wide range of enterprise workflows, including search indexing, ETL pipelines, and document previews, and the exploit fires silently in the background the moment a malicious PDF gets ingested. No special configuration and no authentication are required to trigger it.

SysAid's on-premise IT service management platform offers a second example. CVE-2025-2775 and CVE-2025-2776 were added to CISA's Known Exploited Vulnerabilities catalog, confirming active exploitation in the wild, and both flaws exposed organizations running unpatched instances to server compromise with no authentication required. The lesson for software composition analysis is direct: a team auditing its own parser configuration will find a vulnerability buried in a dependency's internal XML handling only if it scans third-party library behavior explicitly.

Blind XXE: the variant scanner-only testing misses most often

Blind XXE is the variant that matters most operationally, because no data ever appears in the HTTP response, and that's exactly the condition under which a scanner checking only response diffs will mark the endpoint clean.

In blind out-of-band XXE, an attacker hosts a malicious DTD on a server they control, uses parameter entities to exfiltrate file contents to that external server, and confirms the finding through a DNS lookup or an HTTP callback. The Akamai CloudTest case traces this progression clearly. In-band attempts to extract data failed. Error-based attempts yielded only partial confirmation. Clean proof arrived only once an out-of-band Interactsh server received an HTTP callback, confirming both that external entities were being processed and that the server could make outbound HTTP requests, even though the HTTP response visible to the attacker looked like an ordinary 500 error. Invicti's vulnerability database frames the same point: a finding confirmed via DNS exfiltration demonstrates that the application processes external entities through a side-channel signal, not through anything visible in the response body.

A scanner that stops checking the moment it sees no HTTP error will report that endpoint as safe. Real testing has to reason about side-channel signals instead: a DNS lookup, an HTTP callback to a server under the tester's control, a timing delay, any signal that proves the vulnerability fired even while the page in front of the attacker looks completely normal. Tools like Burp Collaborator and Interactsh exist specifically to catch this category of finding, built as out-of-band interaction servers designed to surface blind SSRF, blind XXE, and blind command injection alike. A thorough XXE assessment has to map every potential XML input vector across APIs, file uploads, and mobile traffic, then run blind XXE attempts using out-of-band exfiltration and error-based techniques rather than simply sending a payload and reading whatever comes back.

OWASP's reclassification of XXE into Security Misconfiguration correctly names where the root cause lives. Some practitioners have read that reclassification as a sign the vulnerability class has been solved. The active CVEs with confirmed exploitation that surfaced in 2025 argue otherwise: the taxonomy changed, the underlying risk did not.

What whitebox pen testing finds in document processing APIs that blackbox testing cannot

Third-party library XXE, the kind the Tika case illustrates, is especially easy for blackbox testing to miss. The endpoint that triggers it can look like an ordinary file upload, with no XML visible anywhere in the request or response, because the vulnerable parsing happens entirely inside the dependency, out of view.

Cloud configuration adds a second layer a blackbox test can't reach on its own: assessing whether the IAM roles attached to document processing services carry permissions broad enough to make a successful SSRF-to-metadata-service exploit catastrophic. That assessment has to happen before the vulnerability is even confirmed, to understand what's actually at stake if it is. A test that identifies XXE but can't evaluate what credentials the metadata service would hand back, or what those credentials can reach, delivers a finding with a hole in it.

Rigor here means proof, not inference. Every finding needs a working exploit behind it, not a theoretical flag raised from a library version number or a configuration pattern that looks suspicious. An unverified "maybe" doesn't tell an engineering team whether it's actually exposed, and it won't satisfy auditors or enterprise customers asking for evidence of real threat discovery. For XXE specifically, proof means demonstrated data exfiltration: a file read, a response from the metadata endpoint, an out-of-band callback landing on a controlled server. If a parser merely accepts a DTD block, that isn't proof of anything on its own.

Parser hardening that fixes XXE across the surfaces that carry it

The baseline fix holds steady across languages and parser libraries: disable external entity resolution and DTD processing explicitly, rather than trusting default behavior, and apply that setting in every environment the code runs in, including production, staging, CI/CD, and test.

In Java, using SAXParserFactory, set disallow-doctype-decl to true and disable both the external-general-entities and external-parameter-entities features by hand. In.NET, set DtdProcessing to DtdProcessing.Prohibit on XmlReaderSettings. In PHP, avoid relying on the LIBXML_NOENT flag as a safeguard. A documented PHP XXE variant works without LIBXML_NOENT or LIBXML_DTDVALID at all, needing only one of LIBXML_DTDLOAD or LIBXML_DTDATTR to succeed, which shows that disabling the two more commonly cited flags isn't sufficient on its own. Their conclusion was straightforward: these settings have to be present everywhere code runs, with no exceptions carved out for staging or test.

Hardening the parser's code path is only part of the work. Environment and permissions matter just as much, because even a correctly configured parser can become a stepping stone if the process running it has filesystem access to secrets or unrestricted network access to the cloud metadata service. File permissions should prevent the parser's process user from reading /etc/*, application secrets, or private keys. SELinux or AppArmor can restrict what XML-handling processes are allowed to do. Outbound network access for XML-handling containers should be disabled or restricted, to cap the blast radius of any SSRF attempt. The parser should run under least privilege: it doesn't need IAM credentials, so it shouldn't have them.

Third-party libraries need their own discipline: keeping dependencies on patched versions and explicitly auditing whether those libraries expose their own parser configuration options, since a library running its own internal parser instance isn't covered automatically by settings applied to an application's own parser. The Apache Tika case, rated 9.8 Critical by NVD and NIST, demonstrates that any system running an affected tika-core version is exposed regardless of how carefully the surrounding application was written.

Where a protocol or integration doesn't require XML at all, removing it beats hardening it. JSON doesn't support external entity references by design. gRPC and Protocol Buffers have no DTD system to exploit. GraphQL doesn't parse XML. Where XML is unavoidable, because a third-party integration demands it, isolate the parser: sandbox it, restrict its permissions, and validate everything it outputs before that output reaches any application logic.

None of this holds as a one-time fix. Continuous pull-request scanning that checks parser configuration on every code change catches regressions before they reach production, whether that regression comes from a new dependency, a configuration flag someone accidentally removed, or a new XML-handling route added without the same hardening applied elsewhere. Point-in-time annual pen tests find XXE as it exists on the day of the test. A team that adds a SAML integration or a document upload feature in the months between test cycles creates a new surface that sits exposed and undetected until the next assessment finds it, or fails to.

Filed underOWASP Top 10

More in OWASP Top 10