How We Audited a High-Traffic Magento 2 Enterprise Stack on OVH and Mitigated XML External Entity (XXE) injection in old SOAP integrations
Initial Stack Assessment and OVH Environment Deep Dive
Our engagement began with a comprehensive audit of a high-traffic Magento 2 Enterprise Edition (now Adobe Commerce) stack hosted on OVHcloud’s dedicated server infrastructure. The primary concern was a potential XML External Entity (XXE) injection vulnerability, suspected to be present in legacy SOAP integrations that were still in use for certain B2B functionalities. The OVH environment was a complex, multi-server setup typical for large Magento deployments, involving:
- Multiple web servers (Nginx) handling frontend and API traffic.
- Dedicated database servers (MySQL Percona Server).
- Redis for caching and session management.
- Elasticsearch for advanced search capabilities.
- A separate Magento Enterprise SOAP server cluster, which was the focal point of our investigation.
The initial phase involved mapping the network topology, understanding the data flow between services, and identifying all entry points for external data, particularly those processed by XML parsers. We focused on the SOAP endpoints as they were known to interact with external partners and legacy systems, making them prime candidates for injection vectors.
Identifying the XXE Vulnerability in SOAP Integrations
The core of the XXE vulnerability lies in the way XML parsers handle external entities. By default, many XML parsers are configured to resolve external entities, which can lead to attackers including malicious content from external sources. In this Magento 2 context, the SOAP integrations were designed to consume and process XML payloads from various partners. A common pattern for XXE exploitation involves crafting a malicious XML document that, when parsed, instructs the server to fetch and include the content of local files (e.g., `/etc/passwd`, configuration files) or even perform Server-Side Request Forgery (SSRF) by making requests to internal or external services.
Our diagnostic process involved several steps:
- Code Review of SOAP Handlers: We meticulously reviewed the PHP code responsible for handling incoming SOAP requests. This included looking for instances where `SimpleXMLElement`, `DOMDocument`, or other XML parsing libraries were used without proper configuration to disable external entity resolution.
- Traffic Interception and Analysis: Using tools like Wireshark and Burp Suite, we intercepted traffic to the SOAP endpoints. We then crafted and sent malformed XML payloads designed to trigger XXE.
- Log Analysis: Server logs (Nginx access/error logs, PHP-FPM logs, Apache logs if applicable) were scrutinized for any unusual activity, such as attempts to access non-existent files or network connections to unexpected hosts originating from the SOAP server.
A critical finding was the use of `DOMDocument::loadXML()` without the necessary security configurations. The default behavior of `DOMDocument` in older PHP versions (and even in newer ones if not explicitly configured) can be susceptible to XXE. Specifically, the parser might attempt to resolve `` declarations.
Exploitation Scenario and Proof of Concept
To demonstrate the vulnerability, we developed a proof-of-concept (PoC) payload. The goal was to read a sensitive configuration file on the SOAP server. We targeted the `app/etc/env.php` file, which contains database credentials and other sensitive information.
The malicious XML payload looked something like this:
PoC XML Payload for XXE
This payload defines an external entity `xxe` that points to the local file `/app/etc/env.php` using the `php://filter` wrapper. This wrapper allows us to read the content of the file as if it were a stream. The entity is then referenced within the main XML structure, forcing the parser to resolve it.
%dtd; ]>&xxe;
The `evil.dtd` file hosted on an attacker-controlled server would contain:
"> %payload;
When this XML is processed by a vulnerable parser, the `&xxe;` entity would be replaced by the base64-encoded content of `app/etc/env.php`. The `evil.dtd` mechanism is a common technique to exfiltrate data when the parser’s output is not directly visible, by making an HTTP request to the attacker’s server with the data embedded in the URL. In our controlled test environment, we could observe the base64-encoded output directly or via logs if the parser was configured to output it.
Mitigation Strategy: Securing XML Parsers
The most effective way to mitigate XXE vulnerabilities is to configure XML parsers to disable the resolution of external entities and DTDs. For PHP’s `DOMDocument`, this is achieved by setting specific options before loading the XML.
PHP Code Hardening for DOMDocument
We implemented a standardized function to safely load XML, ensuring that external entity processing is disabled. This function should be used wherever XML is parsed from untrusted sources.
function loadXmlSecure(string $xmlString): ?DOMDocument
{
$dom = new DOMDocument();
// Disable external entity loading
$dom->resolveExternals = false;
// Disable loading of external DTDs
$dom->setEntityLoader(false);
// Suppress warnings for malformed XML and load it
// Use LIBXML_NOENT to prevent entity substitution if needed,
// but disabling external entities is the primary defense.
// LIBXML_NOWARNING | LIBXML_NOERROR can be used to suppress parsing errors.
if ($dom->loadXML($xmlString, LIBXML_NOENT | LIBXML_XINCLUDE)) {
return $dom;
} else {
// Log parsing errors if necessary
return null;
}
}
// Example usage within a SOAP handler:
$xmlPayload = $_POST['xml_data']; // Assuming data comes from POST
$dom = loadXmlSecure($xmlPayload);
if ($dom) {
// Process the XML safely
// ...
} else {
// Handle invalid XML
// ...
}
Explanation of `loadXmlSecure` parameters:
$dom->resolveExternals = false;: This is the most critical setting. It prevents the parser from resolving external entities, including those defined in DTDs.$dom->setEntityLoader(false);: This explicitly disables the entity loader, which is another mechanism that can be used to load external resources.LIBXML_NOENT: While this flag *enables* entity substitution, it’s often used in conjunction with disabling external entities to ensure that *internal* entities are still processed if necessary, but it’s crucial that external ones are blocked. In some contexts, if you don’t need *any* entity substitution, you might omit this flag or use `LIBXML_NOENT` with caution. The primary defense is disabling external resolution.LIBXML_XINCLUDE: This flag enables XInclude processing, which can also be a vector for XXE if not properly secured. Disabling it is generally safer unless explicitly required.
For other XML parsing libraries or methods in PHP, the principle remains the same: consult the library’s documentation and explicitly disable external entity resolution and DTD loading.
Server-Level and Network-Level Defenses
While code-level fixes are paramount, a defense-in-depth strategy also involves server and network configurations. For the OVH environment, we implemented the following:
Nginx Configuration for Request Filtering
Nginx can be configured to block requests containing suspicious patterns often associated with XXE attacks. This is a proactive measure that can stop some malicious requests before they even reach the PHP application.
# Inside your Nginx server block for the SOAP API
location ~* ^/soap/ {
# Block requests containing common XXE indicators in the URI or body
# This is a basic example; more sophisticated WAF rules are recommended.
if ($request_uri ~* "(
Note: Relying solely on Nginx regex for security is insufficient. It's a supplementary layer. A dedicated Web Application Firewall (WAF) like ModSecurity with up-to-date rulesets provides much more robust protection against various injection attacks, including XXE.
Firewall Rules and Network Segmentation
On the OVH infrastructure, we reviewed and tightened firewall rules (using `iptables` or OVH's firewall management interface) to ensure that the SOAP servers could only communicate with necessary internal services and external partners. This limits the potential impact of SSRF attacks that might be chained with an XXE vulnerability.
# Example iptables rule to restrict outbound connections from the SOAP server
# Allow established connections
sudo iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow connections to specific partner IPs/ports
# sudo iptables -A OUTPUT -d partner.ip.address -p tcp --dport 443 -j ACCEPT
# Allow connections to internal database servers
# sudo iptables -A OUTPUT -d db.server.ip.address -p tcp --dport 3306 -j ACCEPT
# Allow DNS lookups (if necessary)
# sudo iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
# sudo iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT
# Drop all other outbound traffic by default
sudo iptables -P OUTPUT DROP
This principle of least privilege extends to network access, minimizing the attack surface.
Post-Mitigation Verification and Ongoing Monitoring
After implementing the code changes and server-level hardening, a critical step was to re-test the identified endpoints with the same PoC payloads to confirm that the vulnerability was indeed mitigated. We also performed broader security scans and penetration testing to ensure no new vulnerabilities were introduced and that the existing ones were fully addressed.
Ongoing monitoring is crucial. This includes:
- WAF Log Analysis: Regularly reviewing WAF logs for blocked requests that match XXE patterns.
- Application Error Monitoring: Setting up alerts for unusual XML parsing errors or application exceptions that might indicate attempted exploits.
- Regular Security Audits: Scheduling periodic code reviews and penetration tests, especially when new integrations or third-party services are added.
- Dependency Updates: Keeping PHP, Magento, and all related libraries updated to patch known vulnerabilities.
By combining secure coding practices, robust server configurations, and continuous vigilance, we successfully hardened the Magento 2 Enterprise stack against XXE injection and significantly improved its overall security posture.