Preventing XML External Entity (XXE) Attacks
AI generated
OWASP
0x00
OWASP Top 10 · A05 Security Misconfiguration
Preventing XML External Entity (XXE) Attacks
How external entities in XML get abused, and how to stop it

XML parsers with external entity processing enabled can be abused for file disclosure or SSRF. We explain how XXE works, why many modern libraries disable it by default today, and how to explicitly harden DOMDocument in PHP.

13 min read XXE Hardening DOMDocument

1. What Is an XXE Attack?

XML allows a document to define its own entities, similar to variables. External entities take that a step further and can point at an external resource, such as a local file or a URL. If an XML parser processes these external entities without the developer having deliberately configured it that way, the foundation for an XXE attack is in place.

An attacker defines an external entity in the XML document that points at a sensitive local file such as /etc/passwd or an internal configuration file, then references that entity somewhere whose content ends up visible in the application's response.

2. How an XXE Attack Works in Detail

A typical malicious XML document defines an external entity in the DOCTYPE section using the SYSTEM keyword, followed by a path or a URL. Once that entity is referenced in a text node, a vulnerable parser replaces the reference with the content of the referenced resource and builds it directly into the parsed document tree.

On top of the libxml configuration, the fixed version in the example below rejects any document that contains a DOCTYPE at all, since an invoice or order XML file never legitimately needs a doctype definition. This combination of configuration and business level validation shrinks the attack surface to a minimum, even if the behavior of a future libxml version were to change.


<?php

declare(strict_types=1);

namespace App\Service;

final class InvoiceXmlImporter
{
    // Vulnerable: external entities and network access are not disabled
    public function importVulnerable(string $xmlContent): \DOMDocument
    {
        $document = new \DOMDocument();
        $document->loadXML($xmlContent);

        return $document;
    }

    // Fixed: explicitly block external entities, DTDs, and network access
    public function importSecured(string $xmlContent): \DOMDocument
    {
        $document = new \DOMDocument();

        // Since PHP 8, libxml disables external entities by default,
        // explicit hardening still matters as defense in depth.
        $previousSetting = libxml_use_internal_errors(true);

        $success = $document->loadXML($xmlContent, LIBXML_NONET);

        if ($success === false || $document->doctype !== null) {
            libxml_use_internal_errors($previousSetting);
            throw new \InvalidArgumentException('XML document with DOCTYPE is not allowed.');
        }

        libxml_use_internal_errors($previousSetting);

        return $document;
    }
}

3. Why Many Modern Libraries Already Protect Against It

Since PHP 8.0, the underlying libxml library no longer processes external entities automatically by default, after older PHP versions required an explicit call to libxml_disable_entity_loader. That function is now marked deprecated, since its purpose has largely been absorbed by the new default behavior.

Similar shifts happened across many other language ecosystems: modern XML libraries ship with XXE-safe defaults, since the vulnerability appeared so often over the years that opting in felt safer than opting out. Caution is still warranted, though, since older library versions, legacy code, or alternative XML parsers like SimpleXML can remain vulnerable under certain configurations.

4. Common Entry Points for XXE

XXE shows up anywhere an application accepts and parses XML data from the outside: file uploads in XML, SVG, or DOCX format, SOAP interfaces, RSS and Atom feed imports, and configuration imports delivered as XML. SVG files in particular get overlooked often, even though they are technically full XML documents with DOCTYPE support.

Even seemingly internal interfaces are affected, for example an XML-based import of product data or invoices from an ERP system, if the uploaded file does not exclusively come from trusted internal systems but can, for example, be uploaded by external suppliers.

5. Consequences of a Successful XXE Attack

A successful XXE attack often allows reading arbitrary local files that the PHP process has read access to, including configuration files with database credentials or private keys. If the parser also allows network access, XXE can additionally be used for SSRF attacks against internal systems.

In particularly unfortunate configurations, so-called billion laughs attacks are even possible, where nested entity definitions expand exponentially and push the server into a denial of service condition through memory or CPU exhaustion.

6. Explicit Hardening in PHP

Even though PHP 8 disables external entities by default, it is still worth explicitly using LIBXML_NONET to block any network access performed by the parser, along with a deliberate rejection of documents containing a DOCTYPE for any format that never legitimately needs one.

SimpleXML relies on the same underlying libxml settings as DOMDocument, since both sit on top of the same C library. When XML is processed through simplexml_load_string, the same flags such as LIBXML_NONET should be set and the same DOCTYPE check should be applied just as consistently.

7. The Special Case of SVG Uploads

SVG files are often treated like regular image files in upload forms, but technically they are full XML and can contain DOCTYPE definitions with external entities. If an uploaded SVG file gets parsed server side, for a preview or a resize for example, the same XXE hardening needs to apply as for any other XML import.

A robust additional measure is sanitizing uploaded SVG files before delivery with a dedicated sanitizer library that consistently strips DOCTYPE definitions, script tags, and external references, rather than relying solely on parser configuration.

8. Testing and Code Review for XXE

A targeted test sends a document containing an external entity that points at a known, harmless local file, such as a PHP configuration file, to every XML-processing endpoint and checks whether its content shows up in the response. It also helps to test with an entity pointing at an internal test URL to rule out SSRF through XXE.

During code review, it is worth specifically searching for loadXML, simplexml_load_string, and SOAP calls, to confirm that every one of these call sites either runs on a current PHP version with safe default behavior or is explicitly hardened with LIBXML_NONET and a DOCTYPE rejection.

9. Best Practices and Checklist

Every place that parses XML from a source that is not fully trusted should explicitly set LIBXML_NONET, reject documents containing a DOCTYPE whenever the format has no legitimate need for one, and run on a current PHP version with safe libxml default behavior.

It also helps to handle SVG uploads with special care, add an automated XXE test for every XML endpoint, and keep the underlying libxml version updated regularly as part of a robust defense against XXE.

Entry Point Example Risk Mitigation
XML file upload Invoice import via XML Local file disclosure Reject DOCTYPE, LIBXML_NONET
SVG upload Profile picture in SVG format XXE via image endpoint SVG sanitizer plus parser hardening
SOAP interface External partner integration SSRF via XML payload Disable network access in the parser
Feed import Reading an RSS/Atom feed Denial of service via billion laughs Limit entity expansion, block DOCTYPE

Mironsoft

Security audits, OWASP-compliant hardening, and secure architecture

Applications that actually hold up against a real attack attempt?

We review existing applications for classic OWASP vulnerabilities, insecure authentication, and missing input validation, then build an architecture that structurally reduces attack surface instead of just patching individual symptoms.

Security Audit

Systematically checking OWASP Top 10, auth flows, and input validation for vulnerabilities.

Secure Architecture

Building rate limiting, encryption, and access controls correctly from the ground up.

Incident Readiness

Establishing logging, monitoring, and response processes for when things go wrong.

10. Summary

XXE

Root Cause

Parser processes external entities in XML documents.

Detection

Test with an entity referencing a known local file.

Fix

LIBXML_NONET, DOCTYPE rejection, current PHP version.

Prevention

Code review for loadXML calls, SVG sanitizing.

11. FAQ: XXE

1What is an external XML entity?
An external entity is a reference defined in the DOCTYPE section of an XML document that points to an external resource, such as a local file or a URL, which the parser can resolve while processing the document.
2Is PHP automatically protected against XXE since version 8?
The underlying libxml library no longer processes external entities automatically by default since PHP 8, but explicit hardening with LIBXML_NONET and DOCTYPE rejection is still recommended as an additional layer of security.
3What is libxml_disable_entity_loader and is it still needed?
This function had to be called explicitly in older PHP versions to disable external entities. It is now marked deprecated, since the new default behavior of libxml already provides the same protection.
4Can SVG files contain XXE attacks?
Yes, SVG files are full XML documents and can contain DOCTYPE definitions with external entities. When an SVG file is parsed server side, the same risks apply as with any other XML import.
5What is a billion laughs attack?
It involves building nested entity definitions so that resolving them expands exponentially, pushing the server into memory or CPU exhaustion, without requiring any external resource at all.
6Is rejecting DOCTYPE definitions enough on its own?
For most business formats such as invoice or product imports, yes, since these normally never need a doctype. LIBXML_NONET should still be set as well to rule out network access entirely.
7Do the same risks apply to SimpleXML?
Yes, SimpleXML uses the same underlying libxml library as DOMDocument, so the same configuration recommendations and risks apply.
8How do you specifically test an application for XXE?
By sending a document with an external entity pointing at a known, harmless local file to every XML-processing endpoint and checking whether its content appears in the response.
9Which formats besides classic XML are affected?
SVG, DOCX, and other XML-based file formats, as well as SOAP messages and XML-based feed formats like RSS and Atom, are equally at risk.
10How does XXE relate to SSRF?
If the XML parser allows network access while resolving external entities, an XXE flaw can be used to make requests against internal systems, which effectively amounts to an SSRF attack.