Safeguard
Security

XXE Attack Example: How XML External Entity Injection Works and How to Stop It

A defensive XXE attack example that explains how XML External Entity injection abuses parsers, what it can expose, and the parser settings that shut it down.

Priya Mehta
Security Analyst
6 min read

An XXE attack example, at its core, is a malicious XML document that defines an external entity pointing at a resource the application was never meant to expose, and a misconfigured parser that dutifully resolves it. XML External Entity injection (XXE) lands on the OWASP radar because so many parsers resolve external entities by default, and developers rarely turn that behavior off. This walkthrough is deliberately defensive: it explains the mechanism and shows the fix, not how to weaponize it against a live system.

The parser feature being abused

XML has a legitimate feature called entities, similar to variables. A document can define an entity and reference it by name. Entities come in two flavors, and the dangerous one is the external entity, which tells the parser to fetch content from a URI — a local file path or a network location — and substitute it into the document.

That capability made sense in the era when XML was used for large, shared document sets. On a modern web backend parsing untrusted input, it is a foot-gun. If an attacker controls the XML your application parses, and your parser resolves external entities, the attacker can make your server read files or make outbound requests on their behalf.

The conceptual structure of a malicious document looks like this:

<?xml version="1.0"?>
<!DOCTYPE data [
  <!ENTITY x SYSTEM "file:///etc/hostname">
]>
<data>&x;</data>

Here the entity x is declared as a SYSTEM external entity pointing at a local file. When a vulnerable parser expands &x;, it reads that file and folds the contents into the parsed result. If the application then echoes the parsed value back — in an error message, an API response, a rendered page — the file contents leak to the attacker. This snippet is for understanding the mechanism, not a payload to run against systems you do not own.

What XXE can lead to

The impact of XXE depends on how the parsed data flows through the application, but the common outcomes are:

  • Local file disclosure: reading configuration files, credentials, or source that the app process can access.
  • Server-side request forgery (SSRF): pointing an entity at an internal URL so the server makes a request an outsider could not, including cloud metadata endpoints.
  • Denial of service: the classic "billion laughs" style attack uses nested internal entities to exhaust memory, no external fetch required.

Where XXE hides is anywhere untrusted XML gets parsed: SOAP endpoints, SAML assertions, SVG uploads, DOCX and XLSX files (which are ZIP archives full of XML), RSS ingestion, and configuration import features. Teams often harden their obvious API but forget the file-upload path that unzips an office document and parses its parts.

Detecting XXE exposure

You find XXE risk two ways: by inspecting how parsers are configured in your code, and by testing running endpoints. On the code side, search for the XML parser factories in your stack and check whether external entity resolution and DTD processing are disabled. In Java that means auditing DocumentBuilderFactory, SAXParserFactory, and XMLInputFactory usages; in Python, checking whether lxml or the standard library xml modules are fed untrusted input; in .NET, reviewing XmlReaderSettings.DtdProcessing.

On the dynamic side, a DAST scanner such as Safeguard's can probe XML-accepting endpoints for entity resolution behavior without you hand-crafting requests, and it will catch regressions when a future refactor swaps in a differently configured parser. Static analysis complements this by flagging the insecure factory configuration at the line where it is created, before it ever ships.

The fix: disable external entities

The reliable remediation is to configure your XML parser to reject DTDs and external entities entirely. You almost never need them when parsing untrusted input. In Java, the hardened DocumentBuilderFactory setup looks like this:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// Preferred: forbid DTDs completely
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// Belt and suspenders: disable external entity resolution
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);

The equivalent principle applies everywhere: in Python, use defusedxml in place of the standard parsers for untrusted input; in .NET, set DtdProcessing = DtdProcessing.Prohibit; in libxml2-based tools, avoid the NOENT and network-fetch options. If your application genuinely needs DTDs for trusted internal documents, isolate that parsing path and never let untrusted input reach it.

Building XXE prevention into the pipeline

One-time fixes rot. A parser hardened today gets replaced by a well-meaning refactor that instantiates a default factory tomorrow. Bake prevention into the workflow instead:

  • Add a lint or SAST rule that flags any XML parser created without the DTD-disabling features.
  • Keep a short, reviewed helper (a SafeXmlParsers utility) and forbid direct factory instantiation in code review.
  • Include XML-accepting endpoints in your DAST scope so regressions are caught automatically.
  • Remember the indirect surfaces — file uploads, document processing, federation — not just the JSON-looking API that happens to accept XML.

XXE is one of the more preventable vulnerability classes because the fix is a configuration flag, not an architecture change. The reason it persists is that parsers ship insecure by default and the risky endpoints are easy to overlook.

FAQ

What is an XXE attack in simple terms?

It is an attack where a malicious XML document defines an external entity pointing at a file or URL, and a misconfigured parser resolves it — letting an attacker read local files, force server-side requests, or crash the parser.

What can an XXE attack lead to?

Local file disclosure, server-side request forgery against internal services or cloud metadata endpoints, and denial of service through recursively nested entities. The exact impact depends on how the parsed data flows through the application.

How do I prevent XXE?

Disable DTD processing and external entity resolution in your XML parser. Prefer a factory configured to forbid DOCTYPE declarations entirely, use hardened libraries like defusedxml in Python, and keep untrusted XML away from any parser that needs DTDs.

Where does XXE hide besides obvious XML APIs?

In SAML assertions, SOAP services, SVG uploads, RSS feeds, and office documents such as DOCX and XLSX, which are ZIP archives containing XML. File-upload and document-processing paths are common blind spots.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.