Skip to content

Defending Against XXE

XXE is almost always a parser-configuration problem, not a coding one: the application hands attacker-controlled XML to a library whose external-entity and DTD processing is switched on. That makes it one of the easier classes to shut cleanly - turn those features off in the parser (ideally disable DTDs entirely), keep the library current, and the file-read/SSRF/DoS primitives in this cluster simply stop resolving.

1. Disable DTDs and external entities in the parser

Section titled “1. Disable DTDs and external entities in the parser”

The complete fix. Configure the XML parser to reject document type definitions; if the app legitimately needs DTDs, you must then disable external general entities, external parameter entities, and external DTD loading - the last one matters, because an allowed DOCTYPE can still pull in an external DTD (an outbound fetch / SSRF, and the carrier for blind parameter-entity exfil) even with the entity flags off. The exact switch is per-library - use the official setting, don’t hand-roll:

// Java - the strongest single switch: forbid DOCTYPE entirely
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// If DOCTYPE MUST be allowed, drop the line above and instead disable all three:
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); // external DTD fetch (SSRF)
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
// JAXP belt-and-braces: forbid any external access
dbf.setAttribute(javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD, "");
dbf.setAttribute(javax.xml.XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");

Equivalents:

  • Python: parse with defusedxml instead of the stdlib (xml.etree, lxml with resolvers) - it disables entities and DTDs by default.
  • .NET: on XmlReaderSettings, set DtdProcessing = DtdProcessing.Prohibit (the default on modern framework versions); don’t set an XmlResolver.
  • PHP (libxml): avoid the deprecated libxml_disable_entity_loader() dance - on modern libxml (2.9+) external entities are off by default; don’t re-enable them with LIBXML_NOENT.
  • libxml2 generally: don’t pass XML_PARSE_NOENT / XML_PARSE_DTDLOAD.

Harden all of: custom DTDs, external general and parameter entities, external DTD loading (the SSRF/blind-exfil carrier when DOCTYPE is permitted), XInclude, and entity-reference expansion (the last blocks billion-laughs entity-expansion DoS).

2. Keep XML libraries and components current

Section titled “2. Keep XML libraries and components current”

Because XXE is a library-behaviour issue, patching is unusually effective here:

  • Modern parsers increasingly disable external entities by default (libxml 2.9+, recent .NET); an out-of-date library is often the whole reason a target is vulnerable.
  • This extends to everything that parses XML downstream: SOAP stacks, SVG/image and document processors, and transitive dependencies. Keep them patched alongside the core parser.

Error-based XXE (blind exfil) exfiltrates file contents through parser error messages. Disable runtime/verbose error display in production and return generic errors, so a forced parse failure can’t carry data back.

4. Reduce the attack surface and add depth

Section titled “4. Reduce the attack surface and add depth”
  • Prefer JSON where a format choice exists; dropping XML (and XML-based APIs like SOAP) removes the class outright for that endpoint.
  • WAF can block classic <!DOCTYPE ... <!ENTITY patterns - a secondary layer that is evadable, never the fix.
  • Egress controls: blocking outbound traffic from the app server limits blind/OOB exfil and SSRF even if a parser is misconfigured.
  1. Primary: disable DTD processing (or, if DOCTYPE must stay, external general + parameter entities, external DTD loading, and XInclude) in every XML parser the app uses, via the library’s official setting; prefer a hardened wrapper like defusedxml.
  2. Coverage: apply it to every XML sink - SOAP, SVG/image, document, and PDF processors included, not just the obvious endpoint.
  3. Patch: update XML libraries and XML-consuming components to current versions.
  4. Containment: suppress verbose parser errors; restrict the app server’s outbound network access; WAF as a secondary layer.

Avoid presenting a WAF rule as the fix - the parser configuration is the fix; the WAF only buys time.