XML Entity Expansion in NodeJS
Vulnerable example
The following Express.js route parses XML data coming from a POST request and returns the parsed content in the HTTP response. In the example below, libxmljs
is configured with external entity processing enabled:
app.post('/load_xml', upload.single('xml'), async function (req, res) {
if (!req.file) {
res.sendStatus(500);
return;
}
try {
const xml = req.file.buffer;
const doc = libxmljs.parseXml(xml, {noent: true});
res.send(doc.text());
} catch (err) {
res.send(err.toString());
res.sendStatus(500);
}
});
When the function libxmljs.parseXml
is invoked, its logic parses and expands the external entities. For example, when uploading the XML document below:
<!DOCTYPE d [<!ENTITY e SYSTEM "file:///etc/passwd">]><t>&e;</t>
The entity &e;
is expanded with the content of the local /etc/passwd
system file, resulting in the disclosure of the file.
Prevention
Node.js does not provide a native XML parser. Third-party libraries that provide libxml bindings can be used, for example libxmljs.
Parsing of external entities is disabled by default; care must be taken to avoid processing untrusted XML data when this option is enabled. Parsing of external entities can be enabled as follows:
const doc = libxmljs.parseXml(xml, {noent: false});
References
OWASP - XML External Entity (XXE) Processing OWASP - XML External Entity Prevention Cheat Sheet