xml注入本身不直接执行sql,但当解析后的xml内容(如$xml->user_id)被拼接到sql语句中,且未使用参数化查询时,恶意节点内容(如1' or '1'='1)可触发sql注入;若启用外部实体(xxe),还可读取服务器文件或发起ssrf,形成二次利用;根本防护是禁用外部实体(libxml_noent | libxml_dtdattr)并始终对xml提取的数据采用预编译语句,而非字符串拼接。

XML注入导致SQL执行的典型路径
PHP中XML注入本身不直接执行SQL,但当XML解析结果被拼接到SQL语句中(比如用simplexml_load_string()解析后取$xml->user_id拼进"WHERE id = '$id'"),攻击者就能通过构造恶意XML节点内容触发SQL注入。更危险的是,如果启用了外部实体(XXE),攻击者还能读取服务器文件(如/etc/passwd)或发起SSRF,再配合业务逻辑形成二次SQL利用。
禁用libxml外部实体必须加LIBXML_NOENT和LIBXML_DTDATTR
只调用libxml_disable_entity_loader(true)在PHP 8.0+已废弃,且无法覆盖所有解析入口;必须在每次解析前显式传参。常见错误是只加LIBXML_NOENT,漏掉LIBXML_DTDATTR,导致DTD属性实体仍可被解析。
simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOENT | LIBXML_DTDATTR)DOMDocument::loadXML($xml, LIBXML_NOENT | LIBXML_DTDATTR)- 若使用
xml_parse()(expat),需确保xml_parser_create()后未启用XML_OPTION_SKIP_TAGSTART等干扰选项
从XML提取数据后必须走参数化查询,不能字符串拼接
即使XML已禁用外部实体,节点内容仍可能含单引号、分号、注释符等。例如<id>1' OR '1'='1</id>若被直接拼进SQL,照样触发注入。修复核心不是“过滤XML”,而是“切断XML内容与SQL语法的耦合”。
- 错误写法:
$sql = "SELECT * FROM users WHERE id = '" . (string)$xml->id . "'"; - 正确写法:
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?"); $stmt->execute([(string)$xml->id]); - 若必须用
mysqli,则先mysqli_real_escape_string()再拼接——但强烈建议改用PDO或MySQLi预处理
对XML结构做白名单校验比依赖过滤更可靠
正则替换<.></.>或用strip_tags()无法应对CDATA段、命名空间、注释嵌套等场景,且易被绕过。应明确允许的节点名、属性名、文本格式,并用DOMDocument遍历验证。
- 检查根节点是否为预期值:
if ($dom->documentElement->tagName !== 'request') { die('Invalid root'); } - 遍历子节点,拒绝未知标签:
foreach ($dom->documentElement->childNodes as $node) { if ($node->nodeType === XML_ELEMENT_NODE && !in_array($node->tagName, ['user_id', 'action'], true)) { die('Forbidden tag'); } } - 对文本内容额外限制长度和字符集:
ctype_alnum((string)$node->textContent) || preg_match('/^[a-z0-9\-_\.@]+$/i', (string)$node->textContent)
最常被忽略的一点:XML解析器默认行为差异。比如simplexml_load_file()会自动加载外部DTD,而simplexml_load_string()不会——但一旦XML里含DOCTYPE声明且未禁用LIBXML_DTDATTR,风险就回来了。别假设“没远程文件就读不了”,本地DTD也能触发实体展开。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











