An XXE attack demo shows what happens when an XML parser is configured to resolve external entities on untrusted input: the attacker embeds an entity definition that points the parser at a local file or an internal URL, and the parser dutifully fetches it and folds the contents into the parsed document. XML external entity injection sits on the OWASP radar because so many parsers ship with entity resolution enabled by default, which turns an ordinary XML endpoint into a file-read and server-side request forgery primitive.
This walkthrough is defensive. It explains the mechanism with an illustrative payload against a deliberately vulnerable parser you would run locally, so you can recognize the pattern and fix it. It is not a recipe for probing systems you do not own.
The mechanism behind XXE
XML lets a document define entities, essentially named placeholders, in a Document Type Definition. Internal entities expand to literal text. External entities tell the parser to fetch content from a URI, using the SYSTEM keyword. When an application parses attacker-controlled XML with external entity resolution on, the attacker controls that URI.
The classic illustrative payload, run only against a local test harness you control, looks like this:
<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY xxe SYSTEM "file:///etc/hostname">
]>
<data>&xxe;</data>
A vulnerable parser resolves &xxe; by reading the file at that path and placing its contents where the entity appears. If the app echoes the parsed value back, the file contents leak to the attacker. Point the SYSTEM URI at an internal address like a cloud metadata endpoint instead of a file, and the same flaw becomes SSRF, reaching services the attacker could never hit directly.
Why parsers are vulnerable by default
The uncomfortable part of any XXE demo is how little the attacker has to do. Many XML libraries, across Java, .NET, PHP, and Python, historically enabled DTD processing and external entity resolution out of the box for spec compliance. A developer parsing an uploaded XML file, a SOAP request, or a SAML assertion inherits that default without realizing external entities are live.
Variants make it worse. Blind XXE exfiltrates data over an out-of-band channel when the response is not reflected. A billion-laughs style entity-expansion payload uses nested internal entities to exhaust memory, a denial-of-service cousin of the same feature. All of them trace back to a parser that trusts entity definitions in untrusted input.
How to shut it down
The fix is direct: disable DTDs and external entity resolution on any parser that touches untrusted XML. The safest configuration rejects DTDs entirely, since an app that does not need them loses nothing.
Java, disabling DTDs on a DocumentBuilderFactory:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
Python, using defusedxml, which is the recommended drop-in for the standard library parsers:
from defusedxml.ElementTree import parse
tree = parse("uploaded.xml") # rejects DTDs and external entities by default
The OWASP XXE Prevention Cheat Sheet documents the exact flags for each major language and parser. Where you can, prefer a less dangerous data format: if a client sends JSON, do not accept XML at all.
Test for it and keep libraries current
Verify the fix rather than assuming it. A DAST scan against a running endpoint can fire benign XXE probes and tell you whether the parser resolves them, which is the honest way to confirm entity resolution is off. In code review, grep for parser instantiation and check that every one that handles external input sets the hardening flags.
XXE also rides in through vulnerable XML libraries and frameworks, so the defense is partly a dependency problem. Keep your XML and SAML libraries patched, since parsers have shipped XXE-adjacent CVEs, and an SCA tool such as Safeguard can flag a vulnerable XML library pulled in transitively before it reaches production. Our software composition analysis writeup covers how those deep dependencies surface.
FAQ
What can an attacker actually do with XXE?
Read local files the application process can access, perform server-side request forgery against internal services and cloud metadata endpoints, and in some cases cause denial of service through entity expansion. The impact depends on what the parser and its host can reach.
Is XXE only a problem for file uploads?
No. Any endpoint that parses XML from an untrusted source is exposed, including SOAP APIs, SAML single sign-on assertions, SVG image processing, and document formats built on XML. Anywhere untrusted XML meets a parser, the risk applies.
How do I fix XXE without breaking my app?
Disable DTD processing and external entity resolution on the parser. If your application genuinely needs internal entities, disable only external general and parameter entities. Most apps can reject DTDs entirely with no functional loss.
Does input validation stop XXE?
Not reliably. Filtering for entity keywords is easy to bypass with encoding or parameter entities. The robust fix is to configure the parser to refuse external entities, not to sanitize the XML text.