XXE 的成因与收口方式
XXE(XML 外部实体注入)利用的是解析器默认允许 DTD 与外部实体:攻击者在 XML 里声明外部实体,解析时就能读取服务器本地文件、向内网发起请求或造成实体膨胀型拒绝服务。各语言解析器都提供了关闭开关,加固思路统一:禁止 DTD 声明、关闭外部普通实体与参数实体、关掉 XInclude 与实体自动扩展,并把安全配置封装成统一入口。
基本信息
| 影响组件 | Java 的 DocumentBuilder 与 SAX、Python 的 XML 库、PHP 的 libxml、.NET 的 XmlReader |
|---|---|
| 关键开关 | disallow-doctype-decl、external-general-entities、external-parameter-entities |
| Python 方案 | 使用 defusedxml 包装,或用 lxml 显式关闭实体解析 |
| PHP 注意 | 不要传入 LIBXML_NOENT,该选项会开启实体替换 |
| 验证方式 | 上传含外部实体的 XML,观察是否发起外部请求或读取本地文件 |
| 工程要求 | 统一封装安全解析工厂,禁止业务代码直接实例化解析器 |
Java(DocumentBuilderFactory)
Java(DocumentBuilderFactory)
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// 禁止 DTD 声明:最彻底的一步,命中直接抛异常
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// 关闭外部普通实体与参数实体
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);Python(defusedxml)
Python(defusedxml)
from defusedxml.ElementTree import parse
tree = parse('upload.xml') # 默认禁用 DTD 与实体解析
# 标准库 xml.etree 不解析外部实体,但仍要防实体膨胀(billion laughs)
# lxml 场景显式关闭:XMLParser(resolve_entities=False, no_network=True)PHP
PHP
// PHP 8 / libxml 2.9+ 默认不加载外部实体
// 避免传入 LIBXML_NOENT:该选项会开启实体替换,等于主动打开风险
$doc = new DOMDocument();
$doc->loadXML($xml); // 保持默认配置即可
// libxml_disable_entity_loader 自 PHP 8.0 起已废弃,无需再调用各解析器核查点
| 组件 | 默认风险 | 加固动作 |
|---|---|---|
| DocumentBuilderFactory | 允许 DTD 与外部实体 | 设置 disallow-doctype-decl 为 true |
| SAXParserFactory | 同上 | 关闭外部实体特性并禁用 XInclude |
| XStream | 默认转换器组可被利用 | 使用白名单与安全框架,并及时升级版本 |
| Python 标准解析 | 实体膨胀风险 | 统一替换为 defusedxml |
| PHP DOMDocument | 取决于 libxml 版本与选项 | 不传 LIBXML_NOENT,保持默认 |
| .NET XmlReader | DtdProcessing 默认行为变化 | 设为 Prohibit,并禁用外部解析器 |
典型使用场景
- 代码审计:全量排查 XML 解析点与配置入口
- 上线前安全测试:上传 XML、调用 SOAP 接口验证是否命中
- 引入第三方 SDK 时核查其内部解析器配置
- 报文网关、文件导入、报表模板等功能的加固
- 老系统整改排期:先关实体再排期换库
- 消息中间件与配置中心的 XML 通道排查
建议统一封装安全解析工厂,业务代码只调用工厂方法。这样新增功能不会绕过加固,也能在升级解析库时统一调整。
排查不要只看一个解析器:SOAP 客户端、模板引擎、消息转换器(如 XStream、Jackson XML)都有自己的解析入口,漏一个等于没加固。