libxml_disable_entity_loader在PHP 7.4+已移除且完全失效;正确方式是针对SimpleXML、DOMDocument分别显式配置禁用外部实体,或PHP 8.0+统一使用libxml_set_external_entity_loader(null)。

libxml_disable_entity_loader 在 PHP 7.4+ 已被移除
直接调用 libxml_disable_entity_loader(true) 在 PHP 7.4 及更新版本会触发 Deprecated: libxml_disable_entity_loader(): This function is deprecated 警告,且**完全失效**。它只在 PHP 5.6–7.3 有效,但即便在这些版本中,也仅影响部分 libxml API(如 simplexml_load_string),对 DOMDocument::loadXML() 等默认行为无效——因为后者不自动继承该全局开关。
正确禁用外部实体的三种可靠方式
必须针对具体 XML 解析器显式配置,不能依赖全局函数:
-
SimpleXML 场景:使用
LIBXML_NOENT | LIBXML_DTDLOAD以外的选项,并确保不传入LIBXML_NOENT(它会加载外部实体);更安全的是显式禁用 DTD:$xml = simplexml_load_string($raw, 'SimpleXMLElement', LIBXML_NONET | LIBXML_NOCDATA);
-
DOMDocument 场景:必须在实例化后、调用
loadXML()或load()前设置属性:$dom = new DOMDocument(); $dom->resolveExternals = false; $dom->substituteEntities = false; $dom->setEntityLoader(function () { return false; }); // PHP 8.0+ $dom->loadXML($raw); -
PHP 8.0+ 统一方案:使用
libxml_set_external_entity_loader(null)替代已废弃函数,它作用于当前请求上下文,对所有后续 libxml 调用生效(包括 SimpleXML 和 DOM):libxml_set_external_entity_loader(null); $xml = simplexml_load_string($raw); // 安全 $dom = new DOMDocument(); $dom->loadXML($raw); // 安全
为什么只设 LIBXML_NONET 不够
LIBXML_NONET 阻止网络请求,但无法阻止本地 DTD 文件或内联实体定义(如 &xxe; 指向 file:///etc/passwd)。真实 XXE 利用常通过如下方式绕过:
]>此时若未禁用 DTD 解析或实体替换,仍会泄露文件内容。必须组合使用:&xxe; ]]>
resolveExternals = false + substituteEntities = false + libxml_set_external_entity_loader(null)。
遗留系统兼容 PHP 7.2–7.3 的折中写法
若无法升级 PHP 版本,需同时处理全局开关和实例配置,避免遗漏:
if (function_exists('libxml_disable_entity_loader')) {
libxml_disable_entity_loader(true);
}
$dom = new DOMDocument();
$dom->resolveExternals = false;
$dom->substituteEntities = false;
$dom->loadXML($raw);注意:此写法在 PHP 8.0+ 会报弃用警告,上线前务必确认 PHP 版本并清理该调用。
实际防御 XXE 的关键不在“关一个开关”,而在于每个 XML 解析入口都显式关闭实体加载能力——尤其是当代码路径涉及多个解析器(如先用 SimpleXML 再转 DOM),容易漏掉某一处。








