1. 引言
本文将深入探讨XML实体的概念、用途及其实现方式。我们将重点了解XML内置的标准实体,以及如何根据需要定义自定义实体。
2. XML的基本结构
XML是一种用于表示任意数据的标记语言,通过分层的XML元素结构实现,每个元素可包含属性。例如:
<part number="1976">
<name>Windscreen Wiper</name>
</part>
这个例子展示了一个名为"part"的元素,它有一个属性"number"和一个嵌套元素"name"。
⚠️ 关键点:XML使用特殊字符管理语法结构。例如元素始终以小于号"<"开始,以大于号">"结束。
但问题来了:这些特殊字符不能直接出现在内容中,否则会导致解析歧义甚至错误。比如尝试用XML表示数学公式:
<math> 1 < x > 5 </math>
这段代码本意表示x的值在1到5之间,但XML解析器无法区分"< x >"是内容还是嵌套元素。
3. 标准XML实体
XML通过实体(Entity) 解决特殊字符问题。实体是表示其他字符的特殊序列,格式为:
- 以
&
开头 - 以
;
结尾 - 中间是实体名称
例如<
表示小于号"<"。XML定义了五个标准实体:
实体 | 表示的字符 |
---|---|
& |
& (和号) |
' |
' (单引号) |
> |
> (大于号) |
< |
< (小于号) |
" |
" (双引号) |
使用实体后,之前的数学公式可正确表示为:
<math> 1 < x > 5 </math>
✅ 现在解析器能明确区分语法结构和内容。
4. 字符实体
除标准实体外,XML还支持通过Unicode码点表示任意字符:
- 十进制格式:
&#数字;
- 十六进制格式:
&#x十六进制;
÷ <!-- 十进制 -->
÷ <!-- 十六进制 -->
这种表示方式特别适用于:
- 非Unicode编码文档(如ISO-8859-1)中插入Unicode字符
- 表示不可见字符(如控制字符U+0000)
- 明确标识组合字符,方便开发者识别
5. 自定义实体
XML允许定义自定义实体,通过指定名称和替换值简化重复内容。但需注意:不当使用可能引发安全风险。
定义自定义实体需要使用文档类型定义(DTD),在XML文档前声明:
<!DOCTYPE example [
....
]>
<part number="1976">
<name>Windscreen Wiper</name>
</part>
DTD中可定义两种实体:内部实体和外部实体。
5.1. 内部实体
直接在DTD中定义名称和替换值:
<!DOCTYPE example [
<!ENTITY windscreen "Windscreen Wiper">
]>
<part number="1976">
<name>&windscreen;</name>
</part>
解析时&windscreen;
会被替换为"Windscreen Wiper"。
5.2. 外部实体
通过外部资源定义替换值:
<!DOCTYPE example [
<!ENTITY windscreen SYSTEM "http://example.com/parts/windscreen.txt">
]>
<part number="1976">
<name>&windscreen;</name>
</part>
解析器会自动从指定URL获取内容替换实体。
5.3. 潜在安全风险
自定义实体虽强大,但处理不可信XML文档时需格外谨慎:
⚠️ XXE注入攻击
攻击者可构造恶意实体读取敏感文件:
<!DOCTYPE example [
<!ENTITY windscreen SYSTEM "file:///etc/passwd">
]>
<part number="1976">
<name>&windscreen;</name>
</part>
此例尝试读取系统密码文件。
⚠️ XML炸弹(DoS攻击)
通过实体嵌套指数级膨胀文档大小:
<!DOCTYPE test [
<!ENTITY a0 "someLargeString">
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;">
<!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;">
<!ENTITY a4 "&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;">
]>
<document>&a4;</document>
&a4;
展开后包含10,000个"someLargeString"(10层嵌套将达100亿个实例,约140GB)。
安全建议
- 处理不可信XML时禁用自定义实体
- 内部文档可谨慎使用,但需评估风险
- 优先使用白名单机制限制可访问资源
6. 总结
本文介绍了XML实体的核心概念:
- 标准实体解决特殊字符编码问题
- 字符实体支持任意Unicode字符表示
- 自定义实体提供内容复用能力,但伴随安全风险
对于有经验的开发者,掌握这些知识能有效避免XML解析中的常见坑点,特别是在处理用户提交的XML时务必做好安全防护。