Java代码审计实战:XML外部实体注入(XXE)深度解析
XML外部实体注入(XXE)是Web应用程序中一种常见但又常常被忽视的漏洞。它利用了XML解析器解析XML文档时,允许引用外部实体这个特性。如果解析器没有禁用外部实体引用,攻击者就可以通过构造恶意XML,从而导致敏感信息泄露、服务器端请求伪造(SSRF)、甚至命令执行。
1. 漏洞的本质:不可信的XML被信任
XML外部实体注入的根源在于XML解析器。XML文档支持使用实体(Entity)来定义可重用的数据。实体分为内部实体和外部实体。内部实体在文档内部定义,而外部实体则可以引用外部资源,例如本地文件或远程URL。当XML解析器没有正确配置,允许处理外部实体时,就产生了XXE漏洞。
一个典型的XXE攻击XML文档:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<user><name>&xxe;</name>
</user>
这段XML代码声明了一个名为xxe
的外部实体,它的值是服务器上的/etc/passwd
文件。当不安全的XML解析器解析这个文档时,它会去读取/etc/passwd
文件的内容,并将其替换到&xxe;
的位置,从而导致敏感文件内容泄露。
2. Java代码中的XXE常见表现形式
在Java中,许多处理XML的库在默认配置下都容易受到XXE攻击。常见的漏洞点通常出现在以下场景:
案例一:使用不安全的DOM或SAX解析器
在传统的Java开发中,使用 javax.xml.parsers
包来解析XML非常普遍。然而,如果开发者没有禁用对外部实体的支持,就会存在XXE漏洞。
// 不安全的DOM解析器代码
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new File("user.xml"));
//...
漏洞分析:
DocumentBuilderFactory.newInstance()创建的实例,在默认情况下通常会启用外部实体处理。攻击者可以向服务器上传或提交一个包含恶意实体的XML文件,当服务器使用这段代码解析时,就会触发XXE。
案例二:使用不安全的XML相关库
除了标准的 javax.xml.parsers
包,许多其他的XML相关库或框架也可能存在类似问题。例如,当Spring框架在处理XML请求时,如果配置不当,也可能导致XXE。
不安全的代码示例:
@PostMapping(path = "/api/user", consumes = "application/xml")
public String createUser(@RequestBody String xmlData) {// 假设内部使用不安全的XML解析库SAXParserFactory spf = SAXParserFactory.newInstance();SAXParser saxParser = spf.newSAXParser();XMLReader xmlReader = saxParser.getXMLReader();// ...
}
漏洞分析:
这段代码接收XML格式的请求体,如果内部的XML解析器没有正确配置,攻击者可以提交一个包含外部实体的XML,从而发起XXE攻击。
案例三:利用XXE进行SSRF攻击
XXE漏洞不仅可以用于读取本地文件,还可以用于发起服务器端请求伪造(SSRF)。攻击者可以通过外部实体引用来请求内部网络资源,例如:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "http://192.168.1.100:8080/admin/user?id=1">
]>
<data>&xxe;</data>
漏洞分析:
当服务器解析这段XML时,它会向内网地址192.168.1.100:8080发起请求。如果内网服务没有访问控制,攻击者就可以利用这种方式探测内网服务、获取内部数据,甚至执行其他恶意操作。
3. 历史案例:Wordpress XML-RPC XXE 漏洞
在早期的Wordpress版本中,其XML-RPC接口存在XXE漏洞。攻击者可以发送一个恶意的XML-RPC请求,利用XXE来读取服务器上的任意文件,例如wp-config.php
,从而获取数据库连接信息。这个漏洞的严重性在于,它允许攻击者在不进行身份验证的情况下,窃取到系统的敏感配置信息,为后续的攻击(如数据库渗透)提供了便利。
4. 审计与加固:构建XXE的防御体系
防御XXE漏洞的核心在于禁用外部实体引用。
审计重点:
寻找XML处理入口:在代码库中搜索所有与XML解析相关的API和库,如
DocumentBuilderFactory
、SAXParserFactory
、XMLReader
,以及Spring MVC中@RequestBody
注解接收application/xml
的地方。追踪数据流:检查所有XML数据来源,看它们是否来自不可信的用户输入。
安全修复方案:
为了彻底杜绝XXE,必须在所有XML解析器中禁用对外部实体和DTD的支持。以下是针对不同解析器的防御方法:
DocumentBuilderFactory
:DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); // 禁用 DTD dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); // 禁用外部通用实体 dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); // 禁用外部参数实体 dbf.setXIncludeAware(false); // 禁用 XInclude
SAXParserFactory
:SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); spf.setFeature("http://xml.org/sax/features/external-general-entities", false); spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
XMLInputFactory
(StAX解析器):XMLInputFactory xif = XMLInputFactory.newFactory(); xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
Spring Framework:
在Spring MVC中,可以使用MappingJackson2XmlHttpMessageConverter或Jaxb2Marshaller来处理XML,并确保它们的底层解析器都进行了安全配置。