XXE: XML external entity attacks and how to disable them

A feature of XML lets a document pull in outside content. If your parser allows it, an attacker can use it. The fix is a parser setting.

An XML document reaching out to files

XML External Entity (XXE) is a vulnerability in applications that parse XML from untrusted sources. It is part of the "security misconfiguration" family, because the dangerous feature is usually enabled by default in older parsers.

The idea

XML has a feature called entities, a kind of shortcut or variable that can be defined in a DTD (Document Type Definition) at the top of the document. An external entity can point to a file or a URL, and the parser fetches it and puts the content into the document.

If the application parses XML supplied by a user and then uses or returns the result, an attacker may be able to make the parser:

  • Read local files on the server (for example configuration files) and include the content in the output or an error.
  • Make network requests from the server to internal systems (this is a form of SSRF).
  • Cause denial of service, for example with nested entity expansion (the "billion laughs" attack).

Where XML hides

  • SOAP and older APIs, SAML single sign-on, RSS and Atom feeds.
  • File formats that are zip files of XML: DOCX, XLSX, PPTX, SVG, ODT.
  • XML-based configuration, SVG image processing and "import" features.
  • Any endpoint that accepts Content-Type: application/xml (and sometimes, JSON endpoints that also accept XML if you change the header).

Prevention

The reliable fix is to configure the XML parser to disable DTDs and external entities.

  • Java: set XMLConstants.FEATURE_SECURE_PROCESSING and disable disallow-doctype-decl features (http://apache.org/xml/features/disallow-doctype-decl = true), and turn off external general and parameter entities.
  • Python: use defusedxml instead of the standard XML parsers on untrusted input.
  • PHP: modern versions of libxml disable external entities by default, but never enable LIBXML_NOENT or LIBXML_DTDLOAD for untrusted data.
  • .NET: set DtdProcessing = DtdProcessing.Prohibit (or Ignore) and use a null XmlResolver.
  • Node.js: choose parsers that do not resolve external entities, and check the documentation of the one you use.

Other defences:

  • Prefer simpler formats like JSON when you can.
  • Validate input and reject documents that contain a DOCTYPE if you do not need it.
  • Keep libraries updated. Defaults have become safer over time.
  • Limit network access from the server (egress filtering), so even if a parser fetches a URL it cannot reach internal systems.
  • Run the application with least privilege, so it cannot read sensitive files anyway.

Testing safely

Use only your own application or a deliberately vulnerable lab. Review parser settings in code and use static analysis, which is safer than probing production.

Takeaway: disable DTDs and external entities in every XML parser that touches untrusted input, and prefer JSON when possible.


Web Security series, Part 2 (more bug classes and hardening): 5 of 6. Previous: Insecure deserialization: why untrusted data should not become objects.

Keep reading