XXE漏洞本质是XML解析器默认加载执行外部实体,需在每次解析前独立禁用:PHP须对每个解析函数调用libxml_disable_entity_loader(true),Java须设disallow-doctype-decl为true,Python须用defusedxml并显式配置resolve_entities=False和no_network=True。

XXE漏洞本质是解析器“照单全收”外部实体
XML解析器默认可能加载并执行用户可控的SYSTEM或PUBLIC实体,只要应用把原始XML喂给解析器且没关掉这个功能,攻击者就能读/etc/passwd、访问http://192.168.1.100:8080、甚至触发SSRF。这不是代码写错,而是配置松懈——就像让快递员直接拆开所有包裹再转交给你。
PHP里libxml_disable_entity_loader(true)必须在解析前调用
很多老项目只在开头写一次,但simplexml_load_string()、DOMDocument::loadXML()、xml_parse()等函数各自独立触发解析,每次调用前都得重置;漏掉一个就等于留个后门。
-
libxml_disable_entity_loader(true)只影响后续解析,不回溯已加载的上下文 - PHP 8.0+ 默认为
true,但升级不等于自动修复——旧代码仍可能显式设为false - 若用了
LIBXML_NOENT或LIBXML_DTDATTR标志,必须确保没同时启用LIBXML_LOADDTD
Java中disallow-doctype-decl比禁用外部实体更彻底
只关external-general-entities不够,攻击者可绕过:用注释包裹DOCTYPE,或用大小写混写ExTeRnAl骗过简单过滤。真正有效的是直接禁止DOCTYPE声明本身。
- 必须设置
setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) - 该配置会直接抛
SAXParseException,而非静默忽略——这是你该看到的日志线索 - 若用Spring的
MarshallingHttpMessageConverter,需确认底层XmlBeanDefinitionReader也继承了该安全特性
Python用defusedxml不是可选项,是默认底线
原生xml.etree.ElementTree不解析外部实体,看似安全,但一旦项目引入lxml(比如用BeautifulSoup解析XML或调用requests的xml响应),风险立刻回归。而defusedxml从设计上就堵死所有路径。
- 替换
from xml.etree import ElementTree为from defusedxml.ElementTree import parse -
lxml用户必须显式传resolve_entities=False和no_network=True,缺一不可 - 别信“我只解析内部数据”——HTTP请求体、文件上传、配置项拼接都可能意外带入用户输入
最常被忽略的点:XXE防御不是一次性配置,而是每个XML解析入口都要独立加固。哪怕同一服务里有五个地方调用解析器,就得检查五次——因为攻击者只找那一个没关严的口子。










