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.
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
SYSTEMidentifiers (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, orapplication/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/jsonbut 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
SYSTEMentities — in documents they control
Detection
- Code review: search for XML parsing functions —
xml.parsers.*,DocumentBuilder,SAXParser,XMLReader,ElementTree,lxml,javax.xml.parsers— and check whetherexpandEntityReferences,external-general-entities, orexternal-parameter-entitiesare explicitly disabled. Default configurations are almost always vulnerable. - Dynamic testing: submit a minimal XXE payload (a
SYSTEMentity 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
SYSTEMentity 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://), disablegeneral-entitiesandparameter-entities, and setexpandEntityReferences=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:
DocumentBuilderFactorywithsetFeature("http://apache.org/xml/features/disallow-doctype-decl", true)in Java,defusedxmlin Python,libxml2withXML_PARSE_NOENT | XML_PARSE_DTDLOADdisabled (notXML_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
SYSTEMentity 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
- MDN: XML introduction
- W3C: Extensible Markup Language (XML) 1.0
- OWASP: XML External Entity (XXE) Processing
- PortSwigger: XML external entity (XXE) injection
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.