On this page
  1. What is XXE?
  2. What an attacker gets from it
  3. How to fix it
  4. Finding it in your own code
  5. Where AI coding assistants get this wrong
  6. Checklist
  7. FAQ
    1. Is XXE still relevant in new applications?
    2. Does escaping the input prevent XXE?
    3. Can a web application firewall block it?
    4. What is blind XXE?
  8. Related topics
  9. Sources
concept

What Is XXE (XML External Entity Injection)?

XML parsers can be told to fetch external resources. XXE abuses that to read local files, probe internal networks, and in some configurations cause denial of service.

Quick answer

  • XXE is an attack against an XML parser that resolves external entities — references in the document that tell the parser to load a resource.
  • The classic impact is file disclosure: an entity pointing at a local file is included in the document and returned to the attacker.
  • A second impact is server-side request forgery: the entity can reference an internal URL, turning the parser into a request proxy.
  • The fix is to disable external entity resolution and DTD processing in the parser, which is a configuration change rather than an input filter.
  • Generated code is exposed when it accepts XML without configuring the parser, which many library defaults make easy to do.

What is XXE?

It is an injection flaw in how an XML document is parsed. XML has a feature called a document type definition, which can declare entities — named references that stand for other content. Some entity types are resolved from outside the document itself, either a local file or a URL, and a parser that honours them will fetch that resource and splice its contents into the document. If the application then echoes part of the document back, the fetched content comes with it.

The vulnerability is therefore not in the data being processed but in the parser’s configuration. An application that parses XML with entity resolution enabled, and that reflects any part of the parsed result, is exploitable. The same document is inert under a parser with external entities disabled, which is why the remediation is a setting rather than a sanitiser.

What an attacker gets from it

File disclosure is the headline. An entity referencing a path such as an application configuration file or a credential store on the server causes the parser to read it, and wherever the value appears in the response — an error message, a parsed field, a callback — the attacker sees it. This is a read primitive against the server’s filesystem, limited to files the process can read.

Server-side request forgery is the second: an entity can reference an internal URL, so the parser makes a request the attacker could not make directly, from inside the network. That reaches internal services, metadata endpoints, and administrative interfaces that are not exposed publicly. In some configurations, nested entities expand into each other and consume memory and CPU until the service fails — a denial-of-service variant that needs no response echoing at all.

<!-- A document that makes a naive parser read a local file. -->
<?xml version="1.0"?>
<!DOCTYPE order [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<order>
  <note>&xxe;</note>
</order>

How to fix it

Disable external entity resolution and DTD processing in every XML parser the application uses. In Java, set the parser’s features to disallow doctype declarations and external entities; in Python, use a parser that does not resolve them by default or configure it explicitly; in .NET, set the resolver to null and disable DTD processing. The exact flags differ by library, and the point is that they must be set deliberately, because defaults vary between versions and languages.

Then prefer a format that does not have the feature. If the interface accepts JSON, accept JSON and leave XML to the integrations that require it. If XML is unavoidable, validate against a schema and reject documents that contain a doctype, since well-formed message XML rarely needs one. Both changes reduce the surface rather than relying on a single parser setting to be correct forever.

# Python: use a parser that does not resolve external entities.
import defusedxml.ElementTree as ET   # hardened drop-in for xml.etree

def parse_order(raw: bytes):
    # Rejects DTDs and entity expansion rather than resolving them.
    return ET.fromstring(raw)

# If you must use the standard library, disable the risky features:
#   import xml.sax
#   parser = xml.sax.make_parser()
#   parser.setFeature(xml.sax.handler.feature_external_ges, False)
#   parser.setFeature(xml.sax.handler.feature_external_pes, False)

Finding it in your own code

Search the codebase for XML parsing entry points, then check the configuration at each one. The places to look are request body parsers, file upload handlers that read images or documents with XML metadata, and any integration that consumes a partner’s feed. A parser created inline with default settings is the finding; a shared factory that is configured correctly is not, unless the configuration misses one of the two features.

Then check whether the parsed output can reach a response. XXE that reads a file is severe when the value is reflected and lower-risk when it is discarded — but lower-risk is not safe, because the request-forgery variant works without reflection. Treat every unconfigured XML parser on a server with network access as a real issue rather than as theoretical.

Where this bites vibecoders

XML support appears in AI-generated code whenever an integration mentions SOAP, an RSS feed, a document format, or a partner API: a parser is instantiated and used, with no visible configuration. Whether that is exploitable depends entirely on the library’s defaults, which vary — so the review question is not “does this look safe?” but “what does this parser do with a doctype?” That is a documentation lookup, and it is the whole check.

Where AI coding assistants get this wrong

  • Instantiating an XML parser with default settings and no entity configuration.
  • Validating input with a schema but still resolving external entities.
  • Assuming a framework’s body parser is safe without checking its XML handling.
  • Reflecting parse errors that include the resolved entity content.
  • Treating XXE as only a file-read issue and ignoring the request-forgery variant.
  • Supporting XML on an endpoint that only ever receives JSON.

Checklist

  • Find every XML parsing entry point in the codebase.
  • Disable external entities and DTD processing at each one.
  • Reject documents containing a doctype where the format does not need one.
  • Check that parse errors do not echo document content.
  • Remove XML support from endpoints that do not require it.
  • Add a test fixture with an external entity that must be rejected.

FAQ

Is XXE still relevant in new applications?

It remains relevant wherever XML is parsed, which is common in integrations with enterprise systems, document formats, and legacy APIs. It is less common in purely JSON applications. The vulnerability is a parser configuration issue, so any new code that parses XML inherits the question regardless of how modern the rest of the stack is.

Does escaping the input prevent XXE?

No. Entity resolution happens during parsing, before any escaping the application applies, so escaping the value afterwards does not prevent the file or URL from being fetched. The fix has to be in the parser configuration. Input validation is worth doing, but it is not the control that stops this class.

Can a web application firewall block it?

A WAF may detect known document patterns, which raises the cost of a naive attempt and does nothing about a variant that evades the signature. The reliable fix is disabling the feature in the parser. Treat a WAF as a layer that reduces noise, not as the remediation for a configuration problem in your own process.

What is blind XXE?

A variant where the response does not reflect the resolved content, so the attacker cannot read the file directly. It works by causing an outbound request to an attacker-controlled server or by using error messages as a channel, which makes it a request-forgery attack with a side channel. It is the reason XXE must be fixed even when nothing is echoed back.

Sources

Share: