XML external entity injection (XXE)
CWE-611OWASP A05:2021Updated August 29, 20265 min read
XML external entity injection (XXE) is a vulnerability where an attacker abuses an XML parser that resolves external entities. With it they read local files, force the server to make requests to internal systems, and in the worst case bring the whole application down.
XML external entity injection, or XXE, is a classic but stubborn vulnerability: an attacker hides an instruction inside an XML document and lets your parser read files or open connections that were never intended. This article explains what XXE is, how such an attack unfolds, what an attacker gains from it, and how to make your parser watertight.
What is XML external entity injection?
XML external entity injection (XXE) is a vulnerability where an attacker abuses an XML parser that resolves external entities. XML supports “entities”: shorthands that the parser replaces with their real value while reading a document. An external entity pulls that value from a source outside the document, a local file or a URL. If the parser processes that without question, the attacker decides which source gets loaded.
Think of a form where you may abbreviate a word and list at the bottom what each abbreviation means. Normally you write “the firm” next to the shorthand “co.”. But the person filling it in may also note: “co. means: read out the contents of the personnel safe and put them where co. appears.” Anyone who processes the form thoughtlessly then reads the safe’s contents aloud. XXE is exactly that trick, only with files on your server.
How does an XXE attack work?
XXE appears wherever an application reads XML from user input with a parser that allows DTDs and external entities: a SOAP integration, a SAML login, an uploaded Office or SVG file, a configuration import, or a REST endpoint that accepts XML alongside JSON. As long as the parser resolves external entities, the endpoint is exploitable.
Vulnerable:
// Parses uploaded XML with a default parser that still resolves external entities
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(request.getInputStream());
String message = doc.getElementsByTagName("message").item(0).getTextContent();
This code simply reads the message out without forbidding the parser anything at all. The attacker then sends not ordinary XML, but a document with its own DOCTYPE and an external entity.
The malicious input:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE message [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<message>&xxe;</message>
While reading, the parser sees the entity &xxe; and obediently replaces it with the contents of file:///etc/passwd. That content lands in the message field and travels back to the attacker in the response. Just as the SYSTEM keyword can point at a file, it can point at a URL (http://169.254.169.254/, say) turning the attack into server-side request forgery.
Safe:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Strongest measure: forbid any DOCTYPE declaration
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// Extra safety net: disable external entities and DTDs
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(request.getInputStream());
The safe version intervenes at the parser itself. The key line is the first: disallow-doctype-decl refuses any document with a DOCTYPE, and without a DTD an attacker simply cannot define an entity anymore. The remaining lines form a safety net in case a framework does not support the DOCTYPE ban: they disable external general and parameter entities and switch off XInclude, a second route for pulling in external content. If your application genuinely processes no DTDs (which is true for the vast majority of applications), that first line alone is already enough.
DOCTYPE declaration.What is the impact of XXE?
The severity of XXE ranges from medium to high, which is why the assessment varies by situation. At the top end, the attack leaks the contents of arbitrary files: configurations with passwords, private keys, /etc/passwd, or source code. Because XXE just as easily requests a URL, it also becomes a stepping stone to SSRF, letting an attacker probe internal services and, in a cloud environment, reach the metadata service with its temporary credentials.
Technically, then, the damage reaches beyond a single file. Through the detour into internal systems an attacker maps the network, and in specific setups, such as the PHP expect module, turns an XXE into running commands on the server. At the bottom of the scale, availability comes under pressure: a billion-laughs document takes down the parser and with it the service. For the business this means leaked data, an attacker working deeper into the network from a single XML endpoint, and in the worst case an application that has fallen over. That XXE sits under OWASP category A05 (Security Misconfiguration) is telling: the vulnerability almost always stems from a parser left on its insecure default.
How do you detect XXE?
During a manual test, a pentester feeds every endpoint that accepts XML a document with its own DOCTYPE and a test entity, then watches what happens. If a file’s contents appear in the response, the case is immediately clear. If the response stays empty, the tester tries the blind variant: an external entity that opens a connection to a server under their own control. If a DNS or HTTP request arrives there, the parser resolves external entities and the endpoint is vulnerable, even with no visible output. File formats that use XML internally (SVG, DOCX, XLSX) are probed too, because an upload feature often exposes the same parser. Automated scanners flag XML endpoints but miss precisely the blind and out-of-band cases that call for manual work. AssistSec includes XXE as standard in a penetration test and deliberately tests for those blind variants.
How do you prevent XXE?
- Forbid the
DOCTYPEdeclaration entirely in your XML parser; this is the strongest and simplest measure. - If that is not possible, explicitly disable external general entities, external parameter entities and DTD processing.
- Switch off XInclude so that second route to external content stays closed.
- Use a simpler format such as JSON where you can; if you do not need XML, do not accept it.
- Check uploaded files that contain XML internally (SVG, Office documents) with a parser configured just as strictly.
- Keep your XML libraries and frameworks up to date, as newer versions increasingly choose a safe default.
- Run the processing service with minimal privileges, so a leaked file or an outbound connection does as little damage as possible.
Sources
Frequently asked questions
What is the difference between XXE and SSRF?
XXE is the vulnerability: an XML parser processes external entities it should refuse. SSRF is often the consequence: with an XXE an attacker forces the server to make requests to internal addresses. XXE can also read local files, which SSRF on its own cannot.
Are modern XML parsers vulnerable to XXE by default?
Many older parsers have historically resolved external entities by default, and that default is precisely the problem. Newer versions increasingly turn it off, but you should not rely on that blindly: disable DTD processing and external entities explicitly in your own code.
What is blind XXE?
In blind XXE the attacker does not see the extracted data directly in the response. The flaw is still exploitable: through an out-of-band channel, such as an external DTD that sends the data to a server the attacker controls, or through error messages that leak the content.
Can XXE lead to remote code execution?
In specific setups, yes, for example when the PHP expect module is loaded. Usually it stops at reading files, SSRF or a denial of service, but in the worst case an attacker runs commands on the server.
Related articles
- VulnerabilitiesCWE-22A01:2021Path traversal (directory traversal)Path traversal (directory traversal) explained: how attackers use ../ to escape the intended folder and read sensitive server files, and how to prevent it.
- VulnerabilitiesCWE-94A03:2021Remote code execution (RCE)Remote code execution (RCE) explained: how attackers run their own commands or code on your server through unvalidated input, and how to prevent it.
- VulnerabilitiesCWE-16A05:2021Security misconfigurationSecurity misconfiguration explained: how default passwords, debug modes and open cloud buckets let attackers in, and how to harden your systems.
- VulnerabilitiesCWE-918A10:2021Server-side request forgery (SSRF)Server-side request forgery (SSRF) explained: how attackers abuse your server to reach internal systems and cloud services, and how to prevent it.