XML 实体(Entity)是一种用于定义可重用内容的机制,它允许在 XML 文档中定义一次,然后在多个地方引用。实体可以提高 XML 文档的可维护性和可读性。
XML 实体的类型
1. 内部实体
内部实体在 DTD 中定义,其值直接包含在 DTD 中。
xml<!DOCTYPE root [ <!ENTITY company "ABC Corporation"> <!ENTITY copyright "Copyright © 2024 ABC Corporation"> ]> <root> <name>&company;</name> <footer>©right;</footer> </root>
2. 外部实体
外部实体引用外部文件中的内容。
xml<!DOCTYPE root [ <!ENTITY header SYSTEM "header.xml"> <!ENTITY footer SYSTEM "footer.xml"> ]> <root> &header; <content>Main content here</content> &footer; </root>
3. 参数实体
参数实体主要用于 DTD 中,以 % 开头。
xml<!DOCTYPE root [ <!ENTITY % commonElements " <!ELEMENT name (#PCDATA)> <!ELEMENT email (#PCDATA)> "> %commonElements; ]>
4. 预定义实体
XML 定义了 5 个预定义实体:
| 实体 | 字符 | 描述 |
|---|---|---|
< | < | 小于号 |
> | > | 大于号 |
& | & | 和号 |
' | ' | 单引号 |
" | " | 双引号 |
xml<data> <comparison>5 < 10</comparison> <quote>She said "Hello"</quote> <ampersand>A & B</ampersand> </data>
实体的定义和使用
内部实体示例
xml<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE letter [ <!ENTITY sender "John Doe"> <!ENTITY recipient "Jane Smith"> <!ENTITY greeting "Dear"> <!ENTITY closing "Sincerely"> ]> <letter> <salutation>&greeting; &recipient;,</salutation> <body> This is a sample letter from &sender; to &recipient;. </body> <signature>&closing;, &sender;</signature> </letter>
外部实体示例
xml<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE book [ <!ENTITY chapter1 SYSTEM "chapter1.xml"> <!ENTITY chapter2 SYSTEM "chapter2.xml"> <!ENTITY chapter3 SYSTEM "chapter3.xml"> ]> <book> <title>Complete Guide</title> &chapter1; &chapter2; &chapter3; </book>
chapter1.xml:
xml<chapter id="1"> <title>Introduction</title> <content>Welcome to the guide...</content> </chapter>
参数实体示例
xml<!DOCTYPE book [ <!ENTITY % bookElements " <!ELEMENT book (title, author+, chapter+)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> <!ELEMENT chapter (title, content)> <!ELEMENT content (#PCDATA)> "> %bookElements; ]>
实体的优点
- 代码重用:避免重复内容
- 易于维护:修改一处即可更新所有引用
- 模块化:可以将内容分解为可管理的部分
- 可读性:使 XML 文档更简洁易读
- 灵活性:可以动态替换内容
实体的缺点
- 安全性:外部实体可能带来安全风险(XXE 攻击)
- 复杂性:增加 XML 文档的复杂性
- 性能:解析实体可能影响性能
- 兼容性:某些解析器可能不支持所有实体类型
安全考虑
XXE 攻击风险
外部实体可能被滥用来读取服务器文件或发起攻击:
xml<!-- 恶意的 XXE 攻击 --> <!DOCTYPE data [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <data> <content>&xxe;</content> </data>
防护措施
-
禁用外部实体
javaDocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); -
使用白名单
xml<!DOCTYPE root [ <!ENTITY % safe SYSTEM "safe.dtd"> %safe; ]> -
输入验证
javaif (xml.contains("<!ENTITY") || xml.contains("SYSTEM")) { throw new SecurityException("Potentially malicious content"); }
最佳实践
1. 合理使用实体
xml<!-- 好的做法:使用实体定义常用内容 --> <!DOCTYPE config [ <!ENTITY company "My Company"> <!ENTITY version "1.0.0"> ]> <config> <application>&company; App</application> <version>&version;</version> </config>
2. 避免过度使用
xml<!-- 不好的做法:过度使用实体 --> <!DOCTYPE root [ <!ENTITY a "A"> <!ENTITY b "B"> <!ENTITY c "C"> <!ENTITY d "D"> ]> <root>&a;&b;&c;&d;</root>
3. 使用有意义的名称
xml<!-- 好的做法:使用有意义的实体名称 --> <!DOCTYPE letter [ <!ENTITY companyName "ABC Corporation"> <!ENTITY currentYear "2024"> ]> <letter> <footer>&companyName; ¤tYear;</footer> </letter>
4. 文档化实体
xml<!-- 在 DTD 中添加注释 --> <!DOCTYPE root [ <!-- Company name - used throughout the document --> <!ENTITY company "ABC Corporation"> <!-- Copyright notice - appears in footer --> <!ENTITY copyright "Copyright © 2024 ABC Corporation"> ]>
实体在 Schema 中的替代方案
使用 XML Schema
XML Schema 不支持实体,但提供了其他机制:
xml<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="config"> <xs:complexType> <xs:sequence> <xs:element name="company" type="xs:string" fixed="ABC Corporation"/> <xs:element name="version" type="xs:string" fixed="1.0.0"/> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
使用 XInclude
XInclude 是 XML 的包含机制,可以替代外部实体:
xml<book xmlns:xi="http://www.w3.org/2001/XInclude"> <title>Complete Guide</title> <xi:include href="chapter1.xml"/> <xi:include href="chapter2.xml"/> <xi:include href="chapter3.xml"/> </book>
总结
XML 实体是一个强大的功能,可以提高 XML 文档的可维护性和可读性。然而,使用实体时需要注意安全性,特别是外部实体可能带来的 XXE 攻击风险。在现代 XML 开发中,建议:
- 优先使用内部实体而非外部实体
- 在生产环境中禁用外部实体
- 使用 XML Schema 或 XInclude 作为替代方案
- 遵循最佳实践,合理使用实体
- 进行充分的安全测试
通过正确使用 XML 实体,可以创建更清晰、更易维护的 XML 文档,同时确保应用程序的安全性。