An XXE (XML External Entity) attack is a technique that exploits XML parsers configured to resolve external entities, allowing an attacker to read local files, interact with internal network services, or exhaust server resources through a maliciously crafted XML document. So what is an XXE attack at the protocol level? It abuses a legitimate XML feature — the Document Type Definition (DTD) — that lets a document declare an "entity" pointing to an external resource, such as a file path or a URL. When an improperly configured parser processes that document, it dutifully fetches or reads the referenced resource and injects its contents into the parsed output, which the application may then return to the attacker. XXE has appeared in the OWASP Top 10 and continues to surface in SOAP APIs, file upload features, SAML implementations, and any endpoint that accepts XML input without hardening the underlying parser.
What Is an XXE Attack in Practical Terms?
In practical terms, an XXE attack is XML external entity injection: an attacker submits XML containing a custom DOCTYPE declaration that defines an entity referencing a file, an internal URL, or a special protocol handler, and the vulnerable parser resolves that reference before the application logic ever sees the data. A basic malicious payload looks like this:
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<foo>&xxe;</foo>
If the application echoes the parsed <foo> element back to the user — in an error message, a search result, or an API response — the contents of /etc/passwd leak directly into the response body. The same mechanism can target file:///c:/windows/win.ini on Windows hosts, internal metadata endpoints like http://169.254.169.254/latest/meta-data/, or arbitrary internal services reachable only from the server's network position, turning a text-processing bug into a full server-side request forgery (SSRF) primitive.
Why Do XML Parsers Allow This by Default?
XML parsers allow this by default because DTD processing and external entity resolution were designed as convenience features long before untrusted XML input over the internet was a serious threat model. Standard libraries such as Java's DocumentBuilderFactory, libxml2 (used by PHP, Python's lxml, and Ruby's Nokogiri), and .NET's XmlDocument historically shipped with DOCTYPE processing, external general entities, and external parameter entities all enabled out of the box. Developers who simply called parse() on incoming XML inherited that dangerous default without realizing it. Vendors have since shifted many defaults — recent versions of libxml2 and .NET disable external entity resolution unless explicitly enabled — but a huge amount of legacy code, custom SDKs, and third-party libraries still ships with the old permissive configuration, which is why XXE remains a recurring finding in penetration tests and bug bounty reports more than a decade after it was first popularized.
What Does a Real XXE Vulnerability Example Look Like?
A real XXE vulnerability example that illustrates the severity of the class is the 2014 Facebook bug bounty report, where a researcher found that Facebook's Open Graph XML parser for Word document previews would resolve external entities, allowing arbitrary file reads and internal port scanning of Facebook's infrastructure — the finding earned one of the largest bounties paid at the time. Another widely cited case is CVE-2022-24785, where the Moment.js Timezone-adjacent Luxon library and several XML-based SSO libraries were found vulnerable to blind XXE, where the response doesn't directly reflect the entity's content but the attacker can confirm data exfiltration via out-of-band DNS or HTTP callbacks: the payload defines an external DTD hosted on an attacker-controlled server, which itself defines a parameter entity that reads a local file and appends it to a URL the parser then requests, leaking the file contents through the outbound request's query string. This out-of-band pattern is common in real assessments precisely because most applications don't echo raw XML back to the client.
What Is the Billion Laughs Attack and How Does It Relate?
The billion laughs attack is a denial-of-service variant of entity abuse that doesn't require external references at all — it relies purely on internal entity expansion to exhaust memory. The classic payload nests ten entity definitions, each referencing the previous one ten times:
<?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;">
<!-- ... continues to lol9 -->
]>
<lolz>&lol9;</lolz>
By the time the parser expands lol9, it is attempting to materialize roughly ten billion repetitions of the string "lol" in memory — a few kilobytes of XML source turning into gigabytes of expanded content, crashing the parser or the entire host process. Because billion laughs relies only on internal entities, disabling external entity resolution alone does not stop it; defenses must also cap entity expansion depth and total expansion size, which is why mature XML parsing guidance treats DTD processing itself, not just external entities, as the root problem to eliminate.
What Are the Most Effective XXE Prevention Techniques?
The most effective XXE prevention technique is disabling DTD processing entirely at the parser configuration level, since almost no modern application actually needs inline DTDs for legitimate functionality. In Java, that means configuring DocumentBuilderFactory, SAXParserFactory, and XMLInputFactory with setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); in PHP with libxml, calling libxml_disable_entity_loader(true) (on older versions) and avoiding LIBXML_NOENT; in Python, using defusedxml instead of the standard library's xml.etree.ElementTree or lxml directly; and in .NET, ensuring XmlResolver is set to null on any XmlReaderSettings used for untrusted input. Beyond parser hardening, effective programs also:
- Validate and allowlist XML input against a strict schema (XSD) before parsing, rejecting anything containing a
DOCTYPEdeclaration outright. - Run XML parsing in a sandboxed or resource-limited process so a billion-laughs-style expansion can't take down the whole application.
- Prefer JSON over XML for new APIs where the data model allows it, eliminating the entire class of risk for that surface.
- Keep XML parsing libraries and their transitive dependencies current, since default-safe behavior has improved release over release but only for versions that actually apply it.
- Treat any SOAP, SAML, RSS/Atom ingestion, Office document (DOCX/XLSX use zipped XML internally), or SVG upload feature as XML attack surface requiring the same scrutiny as a public API endpoint.
How Safeguard Helps
Safeguard approaches XXE risk as a software supply chain problem as much as a code-review problem, because the vulnerable parser is rarely code your team wrote — it's a transitive dependency, a bundled SDK, or a library pulled in by a framework. Safeguard continuously scans dependency graphs and SBOMs to flag XML parsing libraries with known-unsafe default configurations or unpatched CVEs, and correlates that with actual usage in your codebase so security teams can prioritize the components that are reachable from untrusted input rather than chasing every XML library in the tree. For custom code, Safeguard's static analysis identifies parser instantiation patterns that leave DTD processing and external entity resolution enabled, surfacing the exact line and the safe-configuration fix rather than a generic advisory. Combined with policy gates in CI/CD, Safeguard can block a build that introduces a newly vulnerable XML dependency or reverts a previously hardened parser configuration, closing the gap between "we fixed this once" and "this stays fixed" across every service and every release.