Web Security

What Is XML? Syntax, Structure, and Security Implications

A foundational guide to XML: elements, attributes, DTDs, entities, namespaces, and why XML parsers are a frequent source of vulnerabilities.

Beginner5 min·Nyxeara Security Research·2026-09-16·CWE-VERIFY
xmlweb-fundamentalsparsingdtd
Prerequisites
what-is-http

Short answer

XML (Extensible Markup Language) is a text-based format for storing and transporting structured data. It uses tags, attributes, and a tree-like hierarchy — and its DTD (Document Type Definition) system can turn a parser into a vulnerability vector.

The idea in one minute

XML looks a lot like HTML, but with a crucial difference: you invent your own tags. Where HTML has a fixed set (<h1>, <p>, <div>), XML lets you define <invoice>, <planet>, or <pokemon> however you like. The structure — angle brackets, opening and closing tags, attributes, nesting — is the same, but the vocabulary is yours.

What makes XML more powerful than simple key-value formats like JSON is its declarative machinery: a DTD can define what tags are allowed, what values they expect, and — critically — what happens when the parser encounters a reference like &myEntity;. That last part is where the security trouble starts.

How XML actually works

An XML document has three layers:

1. The prolog. An optional declaration at the top: <?xml version="1.0" encoding="UTF-8"?>. It tells the parser what it's dealing with.

2. The body. Elements nested in a tree. Every opening tag must have a matching closing tag (unlike HTML, where <br> is tolerated):

<book>
  <title>Learning XML</title>
  <author>Erik T. Ray</author>
</book>

Elements can have attributes (<book genre="technical">), and text content lives between tags.

3. The DTD. Either inline or referenced externally, the DTD declares the document's structure — and, most importantly, its entities:

<!DOCTYPE book [
  <!ENTITY author "Erik T. Ray">
  <!ENTITY legalDisclaimer SYSTEM "http://internal.local/secret.txt">
]>

Entities are macros. &author; gets replaced with "Erik T. Ray" by the parser. &legalDisclaimer; gets replaced with the contents of a URL the parser fetches at parse time. That entity-level fetch is the root of XXE (XML External Entity) attacks.

The parsing process

Raw XML ──▶ Tokenizer (lexer) ──▶ DTD processor ──▶ Tree builder ──▶ DOM/SAX output
                                    │
                            Entity resolution ──▶ file://, http:// fetches

The tokenizer breaks the byte stream into tokens (<, >, &, text nodes). If a DTD is present, a DTD processor runs alongside, building an entity table. When the tree builder encounters &someEntity;, it asks the DTD processor for the replacement. If that entity is an external entity (declared with SYSTEM), the processor makes an outbound request to resolve it — fetching a file, a URL, or anything else the parser's URI handlers support.

This is a design feature, not a bug. XML was designed in the late 1990s when inter-document references seemed useful. In practice, it means every XML parser is a miniature HTTP client with file-system access, and its behavior is controlled by the document it's parsing — often a document supplied by an untrusted user.

A minimal example

A simple XML document representing a product:

<?xml version="1.0" encoding="UTF-8"?>
<product id="4471">
  <name>Wireless Keyboard</name>
  <price currency="USD">49.99</price>
  <inStock>true</inStock>
</product>

An application that accepts this XML and parses it with default settings will create an in-memory tree of elements, attributes, and text — straightforward and harmless.

But change the document to:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE product [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<product id="4471">
  <name>&xxe;</name>
  <price currency="USD">49.99</price>
  <inStock>true</inStock>
</product>

If the parser is not configured to disable external entity resolution, the <name> element will contain the contents of /etc/passwd instead of "Wireless Keyboard." The operating system's user database just walked out through a product name field.

Why this matters for security

XML parsers are vulnerable by default because their specification requires entity resolution. A parser that fully complies with the XML standard will:

  • Fetch external resources via SYSTEM identifiers (file://, http://, ftp://)
  • Process nested entity declarations, including recursive ones
  • Expand general entities inline, regardless of size

Three classes of attacks arise from this:

  • XXE (XML External Entity): reading local files, performing SSRF through the parser's HTTP client, or exfiltrating data via out-of-band channels
  • Entity expansion (Billion Laughs): a small document (< 1 KB) that expands to gigabytes of in-memory content through nested entity references, causing denial of service
  • DTD smuggling: using inline DTDs to bypass network-level controls, since the DTD arrives inside the document itself

What attackers look for

  • Any endpoint that accepts Content-Type: application/xml, text/xml, or application/xhtml+xml
  • SOAP APIs, SAML assertions, RSS/Atom feed ingestion, SVG uploads, DOCX/XLSX import (Office formats are ZIP archives containing XML)
  • Endpoints where the content type is application/json but the parser also accepts XML (content-type sniffing / polyglot parsing)
  • File upload features that process XML-based formats
  • The presence of a DTD declaration — especially SYSTEM entities — in documents they control

Detection

  • Code review: search for XML parsing functions — xml.parsers.*, DocumentBuilder, SAXParser, XMLReader, ElementTree, lxml, javax.xml.parsers — and check whether expandEntityReferences, external-general-entities, or external-parameter-entities are explicitly disabled. Default configurations are almost always vulnerable.
  • Dynamic testing: submit a minimal XXE payload (a SYSTEM entity pointing at an OAST/canary domain you control) as the request body, then watch for DNS or HTTP callbacks from the server. This catches XXE even when the response is never reflected.
  • Error-based: submit malformed XML that triggers entity resolution and observe error messages that leak file contents or internal paths.

Verification: real vulnerability or false positive?

A parser accepting XML input is not a vulnerability. Confirm by showing one of:

  • An out-of-band callback (DNS or HTTP) fired to your controlled infrastructure from a SYSTEM entity with an external URL
  • File contents from a file:// entity reflected in the response body
  • A measurable timing difference between a small entity expansion and a billion-laughs-sized expansion, confirming the parser expanded nested entities

Without one of these, the fact that the endpoint accepts XML is a feature — not a finding.

Real-world impact

  • Data exfiltration: reading /etc/passwd, application source code, configuration files, cloud instance metadata (via SSRF through the parser)
  • Server-Side Request Forgery: using the parser's HTTP client to reach internal services that the application server can access but the attacker cannot
  • Denial of Service: a single HTTP request carrying a 600-byte XML payload can cause the parser to allocate multiple gigabytes of memory, crashing the server
  • Authentication bypass: in SAML-based systems, XXE can read the signing key or forge assertions

Prevention

  • Disable DTD processing entirely unless your application absolutely needs it. This eliminates XXE, entity expansion, and most XML-specific attacks in one setting.
  • If DTDs are required: disable external entity resolution (http://, file://, ftp://), disable general-entities and parameter-entities, and set expandEntityReferences=false.
  • Prefer a safer format like JSON unless XML is truly needed for interoperability. JSON has no entity mechanism and no equivalent of DTD-based SSRF.
  • Use a hardened parser factory that disables external processing by default: DocumentBuilderFactory with setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) in Java, defusedxml in Python, libxml2 with XML_PARSE_NOENT | XML_PARSE_DTDLOAD disabled (not XML_PARSE_DEFAULT) in C.
  • Validate the document structure against a schema (XSD) after parsing — never trust the document's self-describing structure.

Related vulnerabilities

  • XXE — the direct vulnerability enabled by external entity resolution
  • SSRF — XXE is often a vehicle for SSRF, since the parser's entity resolver is typically a capable HTTP client
  • Denial of Service — entity expansion attacks (Billion Laughs, Quadratic Blowup) are XML-specific DoS vectors
  • File disclosure — reading local files through file:// entity references

Testing methodology (do this safely)

  • Only test XML parsers you own or are authorized to test. An XXE payload against a third-party service is an unauthorized intrusion.
  • Use an OAST/canary domain you control for out-of-band callback detection — do not rely on response reflection alone.
  • Test with a single SYSTEM entity pointing at your canary URL. If a callback arrives, you have confirmed entity resolution is active. Only then escalate to file-reading probes.
  • Document the exact payload, the parser's response, and the out-of-band evidence. A confirmed callback is a verifiable finding; a missing callback in a reflected-response test is inconclusive.

Further reading

Nyxeara perspective

Nyxeara's scan engine treats XML parsing features as high-risk by default. In automated assessments, every endpoint accepting XML input receives a battery of XXE probes — entity declarations pointing at internal files, at OAST canaries for out-of-band confirmation, and at recursive entity definitions for DoS detection. The critical distinction, reflected in our findings model, is between features that accept XML (not a finding) and parsers that resolve external entities (a confirmed vulnerability). Evidence is required: a callback or reflected file content, never a content-type header alone.

Published 2026-09-16 · Updated 2026-09-16