XML External Entity Prevention (Secure XML Parser Configuration)
Description
Configures XML parsers to disable external entity resolution, DTD processing, and XInclude expansion, preventing XXE attacks that exploit default-permissive XML parser behavior to read local files, perform SSRF, or cause denial of service via entity expansion (billion laughs). Covers all major XML parsing APIs: Java (DocumentBuilderFactory, SAXParserFactory, XMLInputFactory with FEATURE_SECURE_PROCESSING and disallow-doctype-decl), Python (defusedxml, lxml with resolve_entities=False), .NET (XmlReaderSettings with DtdProcessing.Prohibit), PHP (libxml_disable_entity_loader), C/C++ (libxml2 without XML_PARSE_NOENT), and Go (xml.Decoder with strict mode). The control must be applied at parser instantiation — not as post-parse validation — because entity resolution occurs during parsing. When XML input is required but DTDs are needed for schema validation, use catalog-based resolution with a local-only catalog that rejects network fetches.
CVSS Vector Impacts
| Metric | Transition | Rationale |
|---|---|---|
| Confidentiality (C) | H → N | External entity resolution is disabled at the parser level; the attacker cannot use file:// or http:// entity references to read local files or probe internal services because the parser rejects all external entity declarations before resolution occurs |
| Availability (A) | H → N | DTD processing is disabled, preventing entity expansion bombs (billion laughs) that cause exponential memory consumption; the parser rejects DOCTYPE declarations before recursive entity expansion begins |
CWE Relationships
Verification
Verify that XML parsers are configured to disable external entities and DTD processing across all application languages in use
# Expected: Java XML parser configured with XXE-prevention features (external DTD/schema access disabled, entity support disabled)
# Expected: Python XML parsing uses defusedxml or explicitly disables entity resolution
# Expected: .NET XML parsing prohibits DTD processing or sets XmlResolver to null
# Expected: PHP XML parsing has external entity loading disabled via libxml_disable_entity_loader(true)
# Expected: Zero matches — LIBXML_NOENT/XML_PARSE_NOENT enables entity substitution (misleading name) and is a vulnerability indicator, not a safety control
# Expected: Go XML parsing present — verify no external entity resolution configuration (Go xml.Decoder does not resolve external entities by default)
# Expected: ^(400|403|422)$
Remediation
cat > <app_source>/src/main/java/com/example/SecureXmlParser.java << 'JAVA'
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.XMLConstants;
public class SecureXmlParser {
public static DocumentBuilderFactory newSecureDocumentBuilderFactory() throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
return dbf;
}
}
JAVA
pip install defusedxml
find <app_source>/ -name '*.py' -exec sed -i 's/from xml\.etree import ElementTree/import defusedxml.ElementTree as ElementTree/g; s/import xml\.etree\.ElementTree/import defusedxml.ElementTree/g; s/from xml\.sax /from defusedxml.sax /g' {} +
printf 'SecRule REQUEST_BODY "<!DOCTYPE[^>]*(?:ENTITY|SYSTEM|PUBLIC)" "id:1317001,phase:2,deny,status:403,log,msg:XXE attempt blocked by CME-1317,severity:CRITICAL"
SecRule REQUEST_BODY "<!\[CDATA\[" "id:1317002,phase:2,deny,status:403,log,msg:CDATA injection blocked by CME-1317,severity:CRITICAL,chain"
SecRule REQUEST_HEADERS:Content-Type "(?:text|application)/xml" ""
' > /etc/httpd/modsecurity.d/local_rules/cme-1317-xxe-block.conf
apachectl configtest && apachectl graceful || { nginx -t && nginx -s reload; }