Improper Control of Document Type Definition
Description
Improper Control of Document Type Definition is an XML processing vulnerability where software does not properly restrict Document Type Definition (DTD) references, allowing attackers to specify arbitrary DTDs. DTDs define the structure and legal elements of XML documents and can contain entity declarations. When processing a DTD, the XML parser may attempt to read or include files from the local system or remote locations. If attackers can control DTD content, they can specify sensitive resources, trigger external requests, or cause resource exhaustion. This vulnerability is closely related to XML External Entity (XXE) attacks and is explicitly prohibited in the SOAP specification.
Risk
This vulnerability enables several attack vectors. Attackers can read arbitrary files from the server by defining external entities that reference local files like /etc/passwd or configuration files containing credentials. Server-Side Request Forgery (SSRF) becomes possible when DTDs specify external URLs, allowing attackers to probe internal networks or interact with internal services. Denial of service through "billion laughs" attacks uses recursive entity definitions to consume excessive memory or CPU. In some cases, attackers may be able to execute code if the parsed data influences application logic. The vulnerability affects any application that processes XML from untrusted sources without proper DTD restrictions.
Solution
Disable DTD processing entirely when parsing untrusted XML—this is the most effective mitigation. In most languages, configure the XML parser to disallow DTDs. If DTDs are required, disable external entity resolution. In Java, set features like "http://apache.org/xml/features/disallow-doctype-decl" to true. In PHP, use libxml_disable_entity_loader(). In .NET, set DtdProcessing to Prohibit. Use allowlists for any external resources that must be accessed. Validate and sanitize XML input before processing. Consider using JSON or other formats that don't support entity expansion when dealing with untrusted input. Apply the principle of least privilege to the parsing process.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Files or Directories - Attackers can read arbitrary system files through external entity references in malicious DTDs. |
| Availability | Scope: Availability Resource Consumption - Recursive entity references in DTDs can trigger excessive CPU or memory consumption (billion laughs attack). |
| Integrity, Confidentiality, Availability | Scope: Integrity, Confidentiality, Availability Execute Unauthorized Code or Commands - DTDs may trigger arbitrary HTTP requests that servers execute, potentially leveraging server trust relationships. |
Example Code
Vulnerable Code
// Vulnerable: DTD processing enabled by default
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
public class VulnerableXMLParser {
public Document parseXML(InputStream xmlInput) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Vulnerable: Default settings allow DTD processing
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(xmlInput);
}
}
// Malicious XML exploiting the vulnerability:
/*
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<data>&xxe;</data>
*/
# Vulnerable: etree allows external entities by default in some configs
from lxml import etree
def vulnerable_parse_xml(xml_string):
# Vulnerable: Default parser may process DTDs
parser = etree.XMLParser()
doc = etree.fromstring(xml_string.encode(), parser)
return doc
# Malicious XML:
# <?xml version="1.0"?>
# <!DOCTYPE foo [
# <!ENTITY xxe SYSTEM "file:///etc/passwd">
# ]>
# <root>&xxe;</root>
// Vulnerable: External entities enabled
<?php
function vulnerableParseXML($xmlString) {
// Vulnerable: External entity loading not disabled
$doc = new DOMDocument();
$doc->loadXML($xmlString); // Processes DTDs by default
return $doc;
}
// Malicious XML could read files:
// <!DOCTYPE foo [
// <!ENTITY xxe SYSTEM "file:///etc/passwd">
// ]>
// <root>&xxe;</root>
?>
// Vulnerable: XmlReader with DTD processing
using System.Xml;
public class VulnerableParser {
public XmlDocument ParseXml(string xmlContent) {
XmlDocument doc = new XmlDocument();
// Vulnerable: DTD processing enabled by default in older .NET
doc.LoadXml(xmlContent);
return doc;
}
}
// Vulnerable: SAX parser with DTDs enabled
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
public class VulnerableSAXParser {
public void parseXML(InputStream input) throws Exception {
XMLReader reader = XMLReaderFactory.createXMLReader();
// Vulnerable: External entities not disabled
InputSource source = new InputSource(input);
reader.parse(source);
}
}
<!-- Malicious DTD: Billion Laughs Attack -->
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
<!-- Expands to billions of "lol" strings, exhausting memory -->
Fixed Code
// Fixed: Disable DTD processing entirely
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
public class FixedXMLParser {
public Document parseXML(InputStream xmlInput) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Fixed: Disable DTD processing
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// Additional hardening
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(xmlInput);
}
}
# Fixed: Disable external entity resolution
from lxml import etree
from defusedxml import ElementTree as DefusedET
def fixed_parse_xml_lxml(xml_string):
# Fixed: Configure parser to disallow entities
parser = etree.XMLParser(
resolve_entities=False,
no_network=True,
dtd_validation=False,
load_dtd=False
)
doc = etree.fromstring(xml_string.encode(), parser)
return doc
def fixed_parse_xml_defused(xml_string):
# Fixed: Use defusedxml library (recommended)
doc = DefusedET.fromstring(xml_string)
return doc
// Fixed: Disable external entity loading
<?php
function fixedParseXML($xmlString) {
// Fixed: Disable external entity loading
$previousValue = libxml_disable_entity_loader(true);
$doc = new DOMDocument();
$doc->loadXML($xmlString, LIBXML_NOENT | LIBXML_DTDLOAD | LIBXML_DTDATTR);
// Restore previous setting
libxml_disable_entity_loader($previousValue);
return $doc;
}
// Better: Use XMLReader with proper settings
function fixedParseXMLReader($xmlString) {
libxml_disable_entity_loader(true);
$reader = new XMLReader();
$reader->XML($xmlString);
// Disable DTD processing
$reader->setParserProperty(XMLReader::LOADDTD, false);
$reader->setParserProperty(XMLReader::DEFAULTATTRS, false);
$reader->setParserProperty(XMLReader::VALIDATE, false);
$reader->setParserProperty(XMLReader::SUBST_ENTITIES, false);
// Process XML...
while ($reader->read()) {
// Handle nodes
}
}
?>
// Fixed: Disable DTD processing in .NET
using System.Xml;
public class FixedParser {
public XmlDocument ParseXml(string xmlContent) {
XmlDocument doc = new XmlDocument();
// Fixed: Configure secure XML reader settings
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit;
settings.XmlResolver = null; // Prevent external resolution
using (StringReader stringReader = new StringReader(xmlContent))
using (XmlReader reader = XmlReader.Create(stringReader, settings)) {
doc.Load(reader);
}
return doc;
}
}
// For .NET Framework 4.5.2+
public class FixedParser452 {
public XmlDocument ParseXml(string xmlContent) {
XmlDocument doc = new XmlDocument();
doc.XmlResolver = null; // Disable external resolution
doc.LoadXml(xmlContent);
return doc;
}
}
// Fixed: SAX parser with DTDs disabled
import org.xml.sax.XMLReader;
import javax.xml.parsers.SAXParserFactory;
public class FixedSAXParser {
public void parseXML(InputStream input) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
// Fixed: Disable DTD processing
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
XMLReader reader = factory.newSAXParser().getXMLReader();
InputSource source = new InputSource(input);
reader.parse(source);
}
}
// Fixed: StAX parser configuration
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
public class FixedStAXParser {
public XMLStreamReader parseXML(InputStream input) throws Exception {
XMLInputFactory factory = XMLInputFactory.newInstance();
// Fixed: Disable external entities and DTD
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
factory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
return factory.createXMLStreamReader(input);
}
}
Related CWEs
- CWE-706: Use of Incorrectly-Resolved Name or Reference (parent)
- CWE-829: Inclusion of Functionality from Untrusted Control Sphere (parent)
- CWE-776: Improper Restriction of Recursive Entity References in DTDs (can precede - billion laughs)
- CWE-611: Improper Restriction of XML External Entity Reference (related XXE)
- CWE-918: Server-Side Request Forgery (can result from SSRF via DTD)
References
- MITRE Corporation. "CWE-827: Improper Control of Document Type Definition." https://cwe.mitre.org/data/definitions/827.html
- OWASP. "XML External Entity (XXE) Prevention Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
- OWASP. "Testing for XML Injection." https://owasp.org/www-project-web-security-testing-guide/