XXE Remediation
Risk Severity
🔴 Critical Fix Effort
⚡ Low (Quick Fix) Est. Time
⏱️ 30-60 minutes Reference
A05:2021 CWE-611
XML External Entity (XXE) attacks exploit vulnerable XML parsers to read local files, perform SSRF, or cause denial of service. The fix is straightforward: disable DTD and external entity processing.
XXE Impact
XXE can lead to reading sensitive files like /etc/passwd or web.config, internal network scanning,
denial of service via billion laughs attack, and in some cases remote code execution.
Understanding XXE
Vulnerable XML
xml
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<data>&xxe;</data>Entity &xxe; is replaced with file contents
Attack Variants
- • Classic XXE: Read local files
- • Blind XXE: Out-of-band data exfiltration
- • Error-based XXE: Data in error messages
- • SSRF via XXE: Internal network access
- • Billion Laughs: DoS via entity expansion
Disable External Entities
Simple Fix
The fix for XXE is remarkably simple: disable DTD processing entirely or disable external entity resolution.
Most applications don't need DTDs, so disabling is safe.
Java
java
// DocumentBuilderFactory
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.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
// SAXParserFactory
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);
// XMLInputFactory (StAX)
XMLInputFactory xif = XMLInputFactory.newInstance();
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);Python
python
# Using defusedxml (recommended - drop-in replacement)
import defusedxml.ElementTree as ET
tree = ET.parse('file.xml')
# Standard library with manual fixes
from lxml import etree
parser = etree.XMLParser(resolve_entities=False, no_network=True)
tree = etree.parse('file.xml', parser)
# For xml.etree (limited protection)
import xml.etree.ElementTree as ET
# Note: xml.etree doesn't support external entities by default,
# but defusedxml provides additional protectionsPHP
php
// Disable external entity loading globally
libxml_disable_entity_loader(true);
// For specific parsing
$doc = new DOMDocument();
$doc->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD);
// Using XMLReader
$reader = new XMLReader();
$reader->setParserProperty(XMLReader::SUBST_ENTITIES, false);.NET / C#
csharp
// XmlReader (safe by default in .NET 4.5.2+)
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit;
settings.XmlResolver = null;
XmlReader reader = XmlReader.Create(stream, settings);
// XmlDocument
XmlDocument doc = new XmlDocument();
doc.XmlResolver = null;
doc.LoadXml(xml);
// XDocument (safe by default)
XDocument xdoc = XDocument.Parse(xml);Node.js
javascript
// Using libxmljs2 with safe options
const libxmljs = require('libxmljs2');
const doc = libxmljs.parseXml(xml, {
noent: false, // Don't expand entities
nonet: true, // Don't fetch network resources
noblanks: true,
nocdata: true
});
// Or use a safe parser like fast-xml-parser
const { XMLParser } = require('fast-xml-parser');
const parser = new XMLParser();
const result = parser.parse(xml);🧪 Testing Verification
XXE Test Payloads
xml
<!-- File read test -->
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<data>&xxe;</data>
<!-- SSRF test -->
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://internal-server/admin">
]>
<!-- Billion laughs DoS test (use with caution) -->
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;">
]>
<!-- Expected result: All should fail or return errors -->Common Mistakes
Only Blocking file://
Attackers can use http://, ftp://, gopher://, or custom protocols
Correct Approach
Disable all external entity processing at the parser level
Trusting Content-Type
XML can be sent with any content type (application/json, image/svg+xml)
Correct Approach
Apply XXE protection to all XML parsing, regardless of content type
XXE Prevention Checklist
- ☐ DTD processing disabled in all XML parsers
- ☐ External entity resolution disabled
- ☐ XInclude disabled
- ☐ Using safe libraries (defusedxml, etc.)
- ☐ All XML endpoints protected (SOAP, SVG, DOCX, etc.)
- ☐ Content type not trusted for determining parser