一、XML文档格式
1.文档结构
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
<!--XML声明-->
<?xml version="1.0" encoding="UTF-8"?>
<!--文档类型定义-->
<!DOCTYPE note [ <!--定义此文档是 note 类型的文档-->
<!ELEMENT note (to,from,heading,body)> <!--定义note元素有四个元素-->
<!ELEMENT to (#PCDATA)> <!--定义to元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)> <!--定义from元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)> <!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)> <!--定义body元素为”#PCDATA”类型-->
]]]>
<!--文档元素-->
<note>
<to>Dave</to>
<from>Tom</from>
<head>Reminder</head>
<body>You are a good man</body>
</note>
DTD(Document Type Definition,文档类型定义)是一种用于描述 XML 文档结构的规范。它定义了 XML 文档中允许出现的元素、属性以及它们之间的关系和约束。
DTD 定义了以下内容(稍微了解):
元素(Elements):DTD 描述了 XML 文档中可以使用的元素,包括元素的名称、序列和可能的子元素。通过 DTD,可以指定元素是否为必需的或可选的,以及它们的顺序。
属性(Attributes):DTD 还可以定义用于元素的属性集合,包括属性的名称、数据类型和默认值。这有助于确保 XML 文档中属性的正确性和一致性。
实体引用(Entity References):DTD 可以定义实体引用,用于表示特殊字符或外部实体的替代符号。这样可以避免在 XML 文档中直接使用这些特殊字符,增强了文档的可读性和可维护性。
CDATA 区域(CDATA Sections):DTD 可以定义 CDATA 区域,其中可以包含任意文本数据,而不需要进行转义处理。这对于存储或传输文本数据非常有用,例如 HTML 代码或其他程序代码片段。
实体声明(Entity Declarations)(XXE漏洞产生的原因):DTD 可以声明内部或外部实体,这些实体可以被引用和重用。实体声明可以包括文本或外部文件的引用,以便在 XML 文档中引用这些实体。
通过使用 DTD,可以规范 XML 文档的结构和内容,并确保符合特定的标准和要求。DTD 具有简单的语法结构,并且易于理解和编写。然而,DTD 的功能有限,不支持一些高级的数据验证和复杂的数据类型。因此,在某些情况下,更先进的模式语言如XML Schema或RELAX NG可能更适合用于定义 XML 文档的结构。
二、XXE漏洞
1.漏洞原理
XXE(XML External Entity)漏洞是一种发生在应用程序中处理 XML 数据时的安全漏洞。当应用程序接受并处理来自用户或外部源的 XML 输入时,如果没有适当的防护措施,恶意用户可以利用这个机会注入恶意代码。XML 允许定义实体,并在解析过程中进行引用。XXE 漏洞经常发生在应用程序允许引用外部实体的情况下,攻击者可以通过指定恶意的实体来执行攻击。
2.修复方法
(1)禁用外部实体和 DTD
在 XML 解析器中禁用外部实体和 DTD 的解析,以防止攻击者注入恶意实体。
例如在Java代码中通过设置 DocumentBuilderFactory 的相关特性,禁用外部实体解析,防止 XXE 漏洞的利用。
(2)输入验证和过滤
对用户输入进行严格的验证和过滤,确保只接受预期的 XML 数据,并避免接受或解析不受信任的内容。
(3)使用安全的 XML 解析器
选择使用安全性更强的 XML 解析器,它们在默认情况下禁用外部实体和 DTD 解析,或提供了相应的配置选项。像 OWASP 提供的 dtd-parser 或其他类似的第三方库,可以安全地解析 XML 数据,并自动禁用外部实体解析。可以使用 "dtd-parser" 提供的 DTDParser 类来解析 XML 数据。
(4)白名单验证
仅允许预定义的实体,或限制实体的解析范围,以减少风险。
(5)隔离用户输入与解析环境
将用户提供的 XML 数据与解析环境(如操作系统文件系统)隔离开来,最小化攻击者能够利用的潜在目标。
3.漏洞代码展示(Java)
使用的XML攻击代码内容
声明了一个名为 foo 的元素,并定义了 xxe 实体。 表示 foo 元素可以包含任何内容。
定义了一个实体 xxe,它引用了一个外部的资源,即/Users/free2e/Desktop/XXEtest/testpass.txt文件。在这里,我们使用 file:// URL协议来指定要读取的文件路径。
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY>
<!ENTITY xxe SYSTEM "file:///Users/free2e/Desktop/XXEtest/testpass.txt">
]>
<foo>&xxe;</foo>
package org.example;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
public class XMLParser {
public static Document processXML(String xmlData) throws Exception {
// 创建一个新的DocumentBuilderFactory实例
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 创建一个新的DocumentBuilder实例
DocumentBuilder builder = factory.newDocumentBuilder();
// 使用提供的xmlData加载和解析XML文档,返回一个Document对象
Document document = builder.parse(xmlData);
// 其他逻辑处理...
return document; // 返回解析后的Document对象
}
public static void main(String[] args) throws Exception {
// 调用processXML方法,并传入要解析的XML文件路径
Document document = processXML("/Users/free2e/Desktop/XXEtest/test.xml");
// 获取所有子元素节点
NodeList childNodes = document.getDocumentElement().getChildNodes();
// 遍历子元素节点并打印节点名称和内容
for (int i = 0; i < childNodes.getLength(); i++) {
System.out.println(childNodes.item(i).getNodeName() + ": " + childNodes.item(i).getTextContent());
}
}
}
执行结果:
4.禁用外部实体进行漏洞修复(Java)
在 Java 中,使用 DocumentBuilderFactory 创建 DocumentBuilder 对象来解析和处理 XML 数据。通过设置一些特定的属性,可以禁用外部实体解析。
以下是一些常见的属性,可以用来禁用外部实体解析:
(1)javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING:将此属性设置为 "true" 可以启用安全处理功能,其中包括禁用外部实体解析。
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
(2)http://apache.org/xml/features/disallow-doctype-decl:将此特性设置为 true 可以禁止解析器处理文档类型声明(DTD)。
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
(3)http://xml.org/sax/features/external-general-entities 和 http://xml.org/sax/features/external-parameter-entities:将这两个特性都设置为 false 可以禁用外部一般实体和参数实体的解析。
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);
通过设置这些属性,DocumentBuilderFactory 将创建一个符合要求的 DocumentBuilder,从而禁用了外部实体解析。请注意,具体的特性名称可能因 XML 解析器的不同而有所差异。当禁用外部实体解析时,XML 解析器将不会处理外部实体,从而有效地防止 XXE 漏洞的利用。
//修复后的java代码
package org.example;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
public class XMLParser {
public static Document processXML(String xmlPath) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 禁用外部实体解析防止XXE漏洞
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder builder = factory.newDocumentBuilder();
// 加载和解析XML文档
File xmlFile = new File(xmlPath);
Document document = builder.parse(xmlFile);
// 其他逻辑处理...
return document;
}
public static void main(String[] args) throws Exception {
String xmlPath = "/Users/free2e/Desktop/XXEtest/test.xml";
Document document = processXML(xmlPath);
NodeList childNodes = document.getDocumentElement().getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
System.out.println(childNodes.item(i).getNodeName() + ": " + childNodes.item(i).getTextContent());
}
}
}
执行结果: