Check that every parser handling untrusted XML rejects DOCTYPE declarations or disables external entities and external DTD loading. The exact setting differs by language and parser, so reviewers need a short per language list. If a secure setting is unsupported, treat that as a failure.
General guidance from OWASP
The OWASP XML External Entity Prevention Cheat Sheet advises disabling DTDs where possible and rejecting DOCTYPE declarations if the parser supports it. If DTDs are required, disable external entity resolution and external DTD loading and limit entity expansion. Keep XInclude off unless explicitly required, and restrict external access in schema validation and XSLT as well as plain parsing. It also says to treat an unsupported security setting as a failure, rather than parsing untrusted XML with partial hardening.
Language checklist
| Language | What to check |
|---|---|
| Java | disallow-doctype-decl set to true on DocumentBuilderFactory and SAXParserFactory. For XMLInputFactory, SUPPORT_DTD and IS_SUPPORTING_EXTERNAL_ENTITIES both false. ACCESS_EXTERNAL properties set to empty strings on TransformerFactory and SchemaFactory |
| .NET | Target framework version, since versions before 4.5.2 are unsafe by default. XmlResolver null, or DtdProcessing set to Prohibit or Ignore on older frameworks |
| Python | Untrusted XML goes through defusedxml. Set forbid_dtd where the API offers it. Enforce input and decompressed size limits |
| PHP | Flags LIBXML_NOENT, LIBXML_DTDLOAD or LIBXML_DTDVALID on untrusted XML without blocking external resources |
Two Java details from the cheat sheet are easy to miss in review. Setting setExpandEntityReferences to false is not a substitute for blocking external resources. And XMLDecoder should not receive untrusted input.
Steps for a review
- Find where XML enters the system: API bodies, file imports, SOAP endpoints, document uploads and configuration parsers.
- Find the parser construction. The security settings are set on the factory, not at the parse call.
- Compare the settings with the table. If the code relies on defaults, look up the default for that version.
- Check schema validation and transformation steps, which are separate from parsing and need their own limits.
- Check size limits. The cheat sheet notes the Python standard library xmlrpc module is vulnerable to decompression bombs.
// red flag: factory used with defaults
DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
// safer: reject DOCTYPE declarations
DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
Other formats built on XML
The OWASP page does not address SVG, DOCX or XLSX. Those formats are XML based, so it is reasonable for a reviewer to ask how the code parses them, but that is an inference and not something the source establishes.
Dependencies matter here
Many XXE issues are fixed by upgrading a parsing library. The cheat sheet itself notes that Python's Expat versions below 2.7.2 may be affected by denial of service issues. Checking library versions belongs in the same review as the settings.
Where Safeguard fits
Safeguard scans dependencies and builds SBOMs, so outdated XML libraries show up in your inventory. Try the free scan at gold.safeguard.sh/scan.
Quick check
Is disabling external entities enough on its own?
It is a core step, but OWASP also advises rejecting DTDs where possible, limiting entity expansion and restricting access in schema and XSLT processing.
What if a parser cannot set the secure option?
OWASP says to treat an unsupported security setting as a failure and not continue with partial hardening.
Sources
Last reviewed: 19 October 2026.