首页/安全/漏洞与攻击面速查/禁用 XML 外部实体配置示例
Web 漏洞原理与防护55 条

禁用 XML 外部实体配置示例

各语言解析器都提供禁用 DTD 与外部实体的开关:Java 设 disallow-doctype-decl,Python 用 defusedxml 等安全包装,PHP 需注意 libxml 版本与加载选项。

漏洞与攻击面速查安全更新于 2026-10-13

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 XmlReaderDtdProcessing 默认行为变化设为 Prohibit,并禁用外部解析器

典型使用场景

  • 代码审计:全量排查 XML 解析点与配置入口
  • 上线前安全测试:上传 XML、调用 SOAP 接口验证是否命中
  • 引入第三方 SDK 时核查其内部解析器配置
  • 报文网关、文件导入、报表模板等功能的加固
  • 老系统整改排期:先关实体再排期换库
  • 消息中间件与配置中心的 XML 通道排查
建议统一封装安全解析工厂,业务代码只调用工厂方法。这样新增功能不会绕过加固,也能在升级解析库时统一调整。
排查不要只看一个解析器:SOAP 客户端、模板引擎、消息转换器(如 XStream、Jackson XML)都有自己的解析入口,漏一个等于没加固。

易错点

  • 只检查一个解析器:SOAP 客户端与其他 XML 库(如 XStream)仍需逐一核查
  • PHP 场景传入 LIBXML_NOENT 会主动开启实体替换,属于要避免的配置
  • 设了 disallow-doctype-decl 就不管其他开关,遇到需要 DTD 的旧报文时被临时改回去且忘记还原
  • 只在测试环境验证,生产环境解析库版本不同,加固实际未生效
  • 业务代码自行实例化解析器绕过安全工厂,新代码把风险又带回来

数据来源与参考

  • OWASP XXE Prevention Cheat Sheet
  • defusedxml 项目文档
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌