CWE-611: Improper Restriction of XML External Entity Reference
Overview
XML External Entity (XXE) injection happens when an application parses XML from an untrusted source with a parser that resolves external entities. Many XML parsers resolve entities declared in a Document Type Definition (DTD) by default, so an attacker who controls the document can declare an entity pointing at a local file or a network address and the parser fetches it. That gives the attacker a way to read arbitrary files, perform Server-Side Request Forgery (SSRF), cause Denial of Service (DoS), or in rare cases execute remote code.
Relationship to Other CWEs
Report an XXE finding against this page rather than against its parent, CWE-610 (Externally Controlled Reference to a Resource in Another Sphere), which has no page here. MITRE records one relationship note worth knowing: CWE-611 and CWE-918 are closely related, because both launch outbound requests to unexpected destinations, but XXE can also be performed client-side or anywhere the software is not acting as a server. MITRE's only recorded peer is CWE-441 (Unintended Proxy or Intermediary ('Confused Deputy')), the general shape of a component that fetches on someone else's behalf; the page beneath it that carries the concrete guidance for the outbound-request case is CWE-918.
The pages around it differ by which part of the XML pipeline is wrong:
- CWE-611 (this page) - the parser resolves entities declared in a document it was handed, so the document decides what the parser reads or fetches
- CWE-918 (Server-Side Request Forgery) - the outbound request treated as the weakness in its own right, where untrusted input chooses the destination of a fetch the application makes deliberately. An
http://entity reaches the same place, but the fix there is to constrain the destination, while the fix here is not to resolve the entity at all - CWE-91 (XML Injection) - untrusted data changes the structure of a document the application builds, or the logic of a query it evaluates, rather than what a parser resolves while reading one. Hardening the parser does nothing for it, and building documents with a safe API does nothing for this page; an application that both emits and consumes XML needs both fixes
- CWE-112 (Missing XML Validation) - the document's shape was never checked against a schema. MITRE does not formally link the two, but they arrive together often enough to be worth separating: schema validation is not a fix for this page, because entities are expanded during parsing, before a validator ever sees the resulting infoset
OWASP Classification
A02:2025 - Security Misconfiguration
Risk
High: What an attacker gets depends on what the parser is allowed to reach:
- Confidential data disclosure: Read
/etc/passwd, config files, application source code, cloud metadata - SSRF attacks: Scan internal networks, access internal services, exploit cloud instance metadata endpoints
- Denial of Service: Billion Laughs attack (exponential entity expansion), external entity recursion
- Remote code execution: In rare cases, combined with PHP
expect://wrapper or similar mechanisms
In cloud environments the SSRF path reaches instance metadata services (AWS, Azure, GCP), which expose sensitive credentials.
Remediation Steps
Core Principle: Disable DTD processing and external entity resolution unless the application genuinely needs them, and keep parsing behavior set by the server rather than by the document.
Locate XML external entity vulnerability
- Find the file, line and XML parsing call the finding points at
- Identify where XML data enters the application: user input, external files, databases, network requests, document uploads
- Trace the data flow from that source to the parser's initialization and its parse call
- Determine which XML parser library is in use (see Language-Specific Guidance)
- Check how that parser is configured: whether DTD processing, external entity resolution and XInclude are left on
Disable external entity processing in XML parsers (Primary Defense)
- Disable DTD processing entirely: the parser rejects any document containing
<!DOCTYPE, which is the safest option - If a DTD is required: disable external entity resolution and external DTD loading
- Disable XInclude processing: block
<xi:include>elements - Disable parameter entity processing
- Apply every security setting before the parser sees any XML data
- Apply it to every parser instance in the application
- This works because a parser that cannot resolve external entities has nothing to act on when a document declares one
- The language pages below carry the exact configuration code for each parser
Eliminate XML processing when possible
- For new work, choose another format where you can:
- Use JSON for API communication and data exchange
- Use YAML with safe loading (
yaml.safe_load()) for configuration - Use Protocol Buffers for structured binary data
- Use plain text or CSV for simple data
- Keep XML where something requires it:
- Legacy systems or industry standards (SOAP, SAML, RSS, SVG)
- Document formats that require XML (Office documents, DOCX, XLSX)
- Digital signatures (XML-DSig)
- Where a redesign is on the table, avoid parsing untrusted XML at all
Add input validation for XML documents (Defense in Depth)
- Validate the document against a strict XSD schema, which constrains element and attribute structure
- Reject documents containing
<!DOCTYPEat the input layer when the application has no use for DTDs. Treat that as a filter catching the obvious case rather than the control the fix rests on: it is a text scan over a structured format, and a document in an encoding the scan does not decode - UTF-16 is the usual one - carries the declaration straight past it to a parser that reads it perfectly well - Scan for
<!ENTITYand<!ELEMENTdeclarations - Cap document size, element count and nesting depth
- Cap entity expansions and entity size, which is what limits expansion-based DoS
- Allowlist the elements and attributes you expect
Apply defense-in-depth protections
- Keep XML parsing libraries up to date (monitor security advisories)
- Replace deprecated or unmaintained libraries
- Use dependency scanning tools (OWASP Dependency-Check, Snyk)
- Run XML processing with least privilege (minimal file system access)
- Apply network egress filtering to prevent SSRF (block access to internal networks, cloud metadata endpoints)
- Monitor and log XML parsing errors and entity resolution attempts
- Set resource limits: memory, CPU time for XML processing
Test and verify XXE protection
- Send a file-disclosure payload,
<!ENTITY xxe SYSTEM "file:///etc/passwd">, and confirm no file contents come back - Send an SSRF payload,
<!ENTITY xxe SYSTEM "http://internal-service/admin">, and confirm the parser makes no outbound HTTP request - Send a Billion Laughs document (exponential entity expansion) and confirm the parser rejects it or times out without consuming excessive memory
- Test cloud metadata endpoint access:
http://169.254.169.254/latest/meta-data/ - Test parameter entity attacks:
<!ENTITY % xxe SYSTEM "file:///etc/passwd"> - Confirm legitimate XML documents without entities still parse correctly
- Re-scan with the security scanner to confirm the finding is resolved, and check for new findings introduced by the change
Basic XXE Test Payloads
File Disclosure
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
Expected result: rejection, or an empty or error response. Never the file contents.
SSRF via XXE Test
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://internal-service:8080/admin">
]>
<root>&xxe;</root>
Expected result: no outbound HTTP request from the parser.
Billion Laughs DoS Test
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
]>
<root>&lol3;</root>
Expected result: rejection or a timeout, with no runaway memory use.
Common Pitfalls
- Hardening one parser instance, not every parser: DTDs and external entities get disabled on the DOM parser used for the main request path, while a SAX parser, a StAX reader, a validator or a third-party XML library elsewhere in the same application is left on defaults. A security setting applied to one parser object does not carry over to another, even in the same codebase, so every instance needs its own explicit configuration.
- Treating XSD schema validation as XXE protection: validating the parsed document against a strict schema constrains element and attribute structure, but a validating parser can still resolve a DTD-declared external entity before or during validation unless DTD and entity processing are disabled independently. Schema validation and entity resolution are separate parser features.
- Blocking SSRF but not local file disclosure: network egress filtering stops the parser reaching internal services, which covers the SSRF variant of XXE. It does nothing for
file://reads of local files, which never leave the host and so never meet a network control. - Disabling general entities but not parameter entities or XInclude: turning off external general-entity resolution blocks the classic
<!ENTITY xxe SYSTEM "...">payload. Parameter entities, used inside the DTD itself, and XInclude are separate parser features that reach the same file-read or SSRF outcome unless each is disabled.
Language-Specific Guidance
Parser configuration and framework patterns for each language:
- C# - XmlReader, XDocument, XmlDocument with DTD disabled
- Go - encoding/xml with secure defaults, entity expansion limits
- Java - DocumentBuilder, SAXParser, XMLStreamReader with XXE prevention
- JavaScript/Node.js -
libxmljs2,xml2jsandfast-xml-parser, none of which is safe as it comes: the options that stop entity expansion and DTD loading have to be set explicitly, and whether the native binding is still maintained is part of the choice - PHP - SimpleXML, DOMDocument, XMLReader with entity loading disabled
- Python - lxml, xml.etree, defusedxml for safe XML parsing