VNX-JAVA-029: XML External Entity (XXE) via DocumentBuilderFactory
Detects DocumentBuilderFactory, SAXParserFactory and XMLReaderFactory parsers that are never hardened (disallow-doctype-decl, external-general-entities false, secure processing or ACCESS_EXTERNAL_DTD), and external entities explicitly switched on. StAX, TransformerFactory and SchemaFactory are covered by VNX-JAVA-004.
| Field | Value |
|---|---|
| Rule ID | VNX-JAVA-029 |
| Severity | High |
| SARIF level | error |
| Kind | sast |
| Languages | Java |
| CWE | CWE-611 |
| CAPEC | CAPEC-183 |
| ATT&CK | T1195.002 |
| CWSS vector | CWSS:1.0/TI:H/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:H/DI:H/EX:H/EC:N/P:H |
| Tags | xxe xml parsing |
| Source | internal/sast/rules/vnx-java-029.rego |
Overview
This rule reports the line that creates a DocumentBuilderFactory, SAXParserFactory or XMLReaderFactory.createXMLReader() parser when the file never hardens it: no setFeature(disallow-doctype-decl, true), setFeature(external-general-entities, false), setFeature(FEATURE_SECURE_PROCESSING, true) or setAttribute(ACCESS_EXTERNAL_DTD, "") on that variable (a feature URI held in a constant counts), none on a parser or reader derived from it within the next 30 lines, and no secure(...)/harden(...) helper given the factory. A factory created and used in one chained expression is reported. It also reports setFeature(external-general-entities, true) on a parser that does not restrict ACCESS_EXTERNAL_DTD. setExpandEntityReferences(false) alone is not accepted as hardening. Imports, comments and string literals are not reported. By default, many JDK XML parser implementations will resolve external entity references declared in a document’s DTD. When an application parses untrusted XML with these defaults, an attacker can supply a crafted XML document that causes the parser to read local files, make outbound network requests, or exhaust server resources through entity expansion (Billion Laughs). This vulnerability is classified as CWE-611 (Improper Restriction of XML External Entity Reference).
The Java standard library ships with multiple XML parsing APIs, DOM (DocumentBuilderFactory), SAX (SAXParserFactory), and StAX, each with its own feature flags for controlling entity processing. Configuring each correctly requires explicit, non-obvious setFeature() calls that are easy to omit, especially when copying boilerplate parsing code. JDK versions prior to 8u191 had XXE-safe defaults in some APIs but not others; relying on JDK version for safety is fragile.
Why This Matters
XXE attacks can lead to server-side file disclosure (/etc/passwd, application config files, private keys), Server-Side Request Forgery (SSRF) to internal services including cloud metadata endpoints, denial of service via recursive entity expansion, and in some configurations remote code execution. SAML authentication libraries parsing XML assertions have historically been prime XXE targets, a successful XXE in a SAML processor can lead to complete authentication bypass.
The impact is amplified in containerized or cloud environments where the application process has access to instance metadata services (AWS http://169.254.169.254, GCP, Azure equivalents). An attacker who retrieves IAM credentials via XXE gains the cloud permissions of the application’s service identity, potentially enabling lateral movement across the entire cloud account.
What Gets Flagged
// FLAGGED: DocumentBuilderFactory with no XXE mitigations
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new InputSource(req.getInputStream()));
// FLAGGED: SAXParserFactory without feature hardening
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser parser = spf.newSAXParser();
parser.parse(inputStream, handler);
// FLAGGED: XMLReader without disabling external entities
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.parse(new InputSource(untrustedInput));
Remediation
- Set
http://apache.org/xml/features/disallow-doctype-decltotrueon the factory. This is the most complete mitigation, it rejects any XML document containing a DOCTYPE declaration, eliminating the attack surface entirely. - If DOCTYPE is required, additionally disable external general and parameter entities via their respective feature flags.
- Wrap
setFeature()calls in a try/catch forParserConfigurationExceptionto handle JDK implementations that do not support a specific feature string, fail closed by throwing an exception rather than continuing with an unsafe parser. - Consider using a safe wrapper library (OWASP’s XML security utilities) that applies hardened defaults automatically.
// SAFE: DocumentBuilderFactory with XXE disabled
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// Disallow DOCTYPE entirely, most complete protection
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// Belt-and-suspenders: also disable external entities if DOCTYPE slips through
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new InputSource(req.getInputStream()));
// SAFE: SAXParserFactory equivalent
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
SAXParser parser = spf.newSAXParser();
References
- CWE-611: Improper Restriction of XML External Entity Reference
- CAPEC-183: XML External Entities Blowup
- OWASP XXE Prevention Cheat Sheet
- OWASP XML Security Cheat Sheet
- PortSwigger Web Security Academy, XXE Injection
- Oracle Java SE Security: Secure XML Processing
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-java-029
// nosec vnx-java-029
Or record an ignore rule: vulnetix ignore add --rule vnx-java-029 --reason "...". See Suppressing findings.