导读:本期聚焦于韩兆瑞创作的《XML注入漏洞是什么?如何防御XXE外部实体注入攻击?》,敬请观看详情。解析不受信任的XML数据时,外部实体引用常常成为攻击者突破边界的关键入口。XML注入漏洞本质上是应用程序在处理XML文档时,没有对文档类型定义中的实体声明进行有效限制,导致攻击者可以声明外部实体并读取本地文件、探测内网端口甚至发起拒绝服务攻击。其中XXE外部实体注入是XML注入中风险最高的一种,它利用XML解析器的外部实体解析能力,将file、http、ftp等协议引入解析流程。本文从XML文档结构与实体机制入手,分析XXE漏洞的触发条件、常见利用方式以及不同编程语言中的危险配置,并给出禁用外部实体、使用白名单校验、升级解析器版本等可落地的防御方案,帮助开发者在不影响正常XML解析功能的前提下,封堵外部实体注入带来的安全风险。

XML注入漏洞源于应用程序在处理XML数据时未对文档结构、实体声明以及外部资源加载行为进行严格限制。攻击者通过构造包含恶意DTD或特殊实体的XML文档,诱导解析器读取本地文件、访问内部网络资源或消耗大量系统内存。其中XXE外部实体注入是XML注入家族中危害最严重的一类,它直接利用XML标准中的外部实体解析机制,把解析器变成攻击者手中的数据读取工具。理解XML注入与XXE的关系,是设计安全XML解析流程的前提。

XML注入漏洞是什么?如何防御XXE外部实体注入攻击?

一、XML注入与XXE漏洞的形成原理

XML注入通常指攻击者能够控制XML文档的内容或结构,并在解析过程中触发非预期行为。与SQL注入类似,XML注入并非由XML语言本身的缺陷造成,而是因为解析器在加载外部资源、处理文档类型定义或执行XPath查询时过度信任输入数据。具体到XXE外部实体注入,其核心问题出现在XML标准中定义的实体机制上。XML文档可以通过DTD声明内部实体和外部实体,外部实体可以指向本地文件路径、远程URL或系统标识符,解析器在展开实体时会尝试读取这些资源。

DTD中的实体声明分为两种:普通实体和参数实体。普通实体在文档内容中引用,参数实体则在DTD内部使用。攻击者最常用的是普通外部实体,声明方式如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>

这段XML声明了一个名为xxe的外部实体,其内容来自本地文件/etc/passwd。当解析器处理<root>元素中的实体引用时,会尝试读取该文件并把内容插入到XML结构中。如果应用随后将解析结果返回给用户,攻击者就能直接看到服务器上的敏感文件。值得注意的是,即使解析结果不直接返回,攻击者还可以利用错误信息、回显差异或带外通道来获取数据。

漏洞的触发条件通常有两个:一是XML解析器允许DTD处理,二是未禁用外部实体解析。许多语言的默认XML解析库出于兼容性考虑,仍会开启这些能力。例如下面的Java代码使用DocumentBuilderFactory解析XML,如果没有额外安全配置,就存在XXE风险:

DocumentBuilderFactory factory = DocumentBuilderFactory.newDocumentBuilder();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new File("input.xml"));
System.out.println(doc.getDocumentElement().getTextContent());

这段代码在接收到恶意XML时,会直接读取外部实体指向的文件。类似的默认风险也存在于Python标准库xml.etree.ElementTree、PHP的SimpleXML、.NET的XmlDocument等解析器中。开发者如果只关注XML格式合法性而忽略实体安全,很容易将解析器变成攻击面。

二、XXE攻击的常见利用方式与危害

文件读取是XXE最直接的利用方式。通过file协议,攻击者可以读取服务器上的任意文本文件,包括配置文件、数据库连接信息、SSH私钥、应用源码等。在Linux系统中常见的目标文件有/etc/passwd、/etc/hosts、/proc/self/environ;在Windows系统中可以尝试读取C:\Windows\win.ini或C:\inetpub\wwwroot\web.config等路径。攻击者不一定需要看到完整内容,只要应用对实体引用结果有部分回显或报错信息中包含文件片段,就能逐步拼凑出敏感数据。

除了读取本地文件,XXE还能用于服务器端请求伪造。攻击者将外部实体的SYSTEM标识符指向一个HTTP或HTTPS地址,解析器就会向该地址发起请求。通过这种方式,攻击者可以让服务器访问内网中不被外部直接访问的服务,例如云元数据接口、内网管理后台、数据库端口等。下面是一个利用外部实体探测内网HTTP服务的示例:

<?xml version="1.0"?>
<!DOCTYPE probe [
  <!ENTITY xxe SYSTEM "http://192.168.1.100:8080/admin">
]>
<root>&xxe;</root>

如果服务器存在内网可达的192.168.1.100,并且该地址的8080端口开放,解析器就会尝试获取该URL的内容。攻击者可以通过响应时间、错误信息或回显变化来判断端口是否开放以及服务类型,从而对内网资产进行侦察。这种利用方式将XML解析器变成了代理,绕过了防火墙对入站流量的限制。

XXE还能造成拒绝服务攻击,典型代表是实体扩展攻击,也被称为Billion Laughs攻击。攻击者声明多个逐级倍增的实体,每个实体引用前一个实体多次,导致解析器在展开实体时消耗指数级的内存和CPU资源。以下是一个简化示例:

<?xml version="1.0"?>
<!DOCTYPE lolz [
  <!ENTITY lol "lol">
  <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
  <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
  <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
  <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
  <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
  <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
  <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
  <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

当解析器展开lol9实体时,需要递归展开lol8十次,lol7一百次,依此类推,最终会生成数十亿个lol字符串,导致内存耗尽。这种攻击对配置了XML解析功能且未限制实体展开的接口影响极大,尤其是允许用户上传XML文件或提交XML格式数据的API服务。

三、防御XXE外部实体注入攻击的实践方案

防御XXE的第一原则是尽可能完全禁用DTD和外部实体。如果业务场景不需要使用DTD,直接关闭DOCTYPE声明处理是最彻底的方案。在Java中,可以通过DocumentBuilderFactory的setFeature方法实现:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new File("input.xml"));

这段配置同时禁用了DOCTYPE声明、外部普通实体、外部参数实体、外部DTD加载以及XInclude,并且不展开实体引用。即使攻击者提交包含恶意DTD的XML,解析器也会直接拒绝处理,从根源上消除了XXE风险。需要注意的是,这些特性名称在不同XML解析器实现中可能略有差异,开发者应根据实际使用的解析器版本进行验证。

Python项目可以使用defusedxml库替换标准库的XML解析模块。defusedxml对ElementTree、minidom等接口进行了安全封装,默认禁用外部实体和DTD,不需要开发者手动设置复杂参数。示例代码如下:

import defusedxml.ElementTree as ET
tree = ET.parse('input.xml')
root = tree.getroot()
print(root.text)

使用defusedxml时,无需额外的setFeature调用,就能获得与标准库接近的API体验,同时避免XXE攻击。对于PHP,可以使用libxml_disable_entity_loader函数在解析前禁用实体加载;对于.NET,可以将XmlReaderSettings的DtdProcessing属性设置为Prohibit或Ignore。无论使用哪种语言,核心思路都是关闭外部实体解析能力,并限制DTD处理范围。

如果业务确实需要支持DTD或外部实体,必须更精细地控制资源加载行为。可以在解析器中实现自定义的EntityResolver,对SYSTEM标识符进行白名单校验,只允许访问指定目录或特定协议。同时限制实体展开深度和次数,防止Billion Laughs攻击。此外,服务端网络层应设置出站限制,阻止解析器主动访问外部URL,降低SSRF利用的成功率。最后,建议定期更新XML解析库版本,关注相关安全公告,并在上线前使用专门的XXE测试用例进行自动化安全扫描,确保新增代码不会重新引入漏洞。

对于无法在代码层完全控制的情况,还可以在部署层面增加防护。例如将XML解析任务放到最小权限的沙箱环境中运行,限制文件系统访问范围;在边缘网关或WAF中增加对XML请求内容的检测,拦截包含DOCTYPE或ENTITY声明的可疑输入;在日志监控中记录所有XML解析异常,以便及时发现攻击尝试。综合运用这些措施,可以显著降低XXE外部实体注入带来的安全风险。

XML注入漏洞XXE外部实体注入防御XXE攻击修改时间:2026-09-22 18:11:58

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。