XML External Entity injection, , is one of the most damaging XML vulnerabilities, and it grows out of a legitimate feature: entities.

Entities and the DOCTYPE

An entity is a named shorthand you can reference in a document. Beyond the five built-in ones (<, &, and so on), a document can declare its own inside a DOCTYPE:

<!DOCTYPE foo [
  <!ENTITY company "Acme Corp">
]>

Then &company; expands to Acme Corp. That is an internal entity, harmless enough. The danger is the external entity, which points somewhere instead of holding a value:

<!ENTITY xxe SYSTEM "file:///etc/passwd">

When a parser configured to resolve entities encounters &xxe;, it will fetch whatever the SYSTEM identifier names and substitute the contents. Point it at a local file and the file's contents get read into the document; point it at an internal URL and the parser makes a request from the server's position on the network.

What an attacker gets

The consequences follow directly. Pointing an external entity at a file path lets an attacker read local files: configuration, credentials, key material. Pointing it at a URL turns the parser into a request engine on the internal network, a server-side request forgery that can reach services a remote attacker could not otherwise touch. More advanced variants use parameter entities and external DTDs to exfiltrate data out of band even when the response is not directly visible. The common thread is that the attacker supplies XML and the trusting parser does the dangerous work.

The defense

The fix is refreshingly simple: do not process the DOCTYPE. Nearly every XML parser has a setting to disable DOCTYPE declarations and external entity resolution entirely, and for input you did not author, that setting should be on. If a DOCTYPE is disallowed, external entities cannot be declared, and the whole class of attack disappears. This is why a security-minded XML tool inspects a document without ever resolving its entities: it reports that a DOCTYPE or an external entity is present, and treats that as a finding to flag, not an instruction to follow. Seeing a SYSTEM or PUBLIC entity in untrusted XML is a red flag on its own.

The parser was configured years before your code ran

XXE is rarely a bug somebody wrote. It is a default somebody inherited: a parser whose entity resolution was enabled because that was the sensible choice when the library was designed, in an era when XML documents came from colleagues rather than from the internet.

That is why the fix is almost never in the handling code and almost always in the parser construction — and why an application can be vulnerable in one deployment and safe in another with identical source, because the platform shipped a different default.

Find where the parser is built, not where the document is read. And check it after every dependency upgrade, because a default that changed in your favour can change back, and nothing in a changelog is addressed to a security assumption you never wrote down.