在Java开发场景下,XML作为一种通用的数据交换格式被广泛应用,无论是接口数据传输还是配置文件读取都经常会用到XML解析。但Java默认的XML解析器往往没有开启安全限制,攻击者可以构造恶意的XML内容发起XXE或者DoS攻击,对应用服务造成严重的安全威胁,因此掌握安全的XML解析配置方法非常重要。

常见的XML解析攻击类型
XXE攻击原理
XXE全称是XML外部实体注入,攻击者可以在XML中定义外部实体,让解析器去加载攻击者指定的外部资源。比如构造一个读取服务器本地文件的实体,当解析器处理这个XML时就会把对应文件的内容返回,造成敏感信息泄露。以下是一个简单的恶意XML示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<foo>&xxe;</foo>
DoS攻击原理
DoS攻击通常利用XML的递归结构或者超大实体定义,让解析器消耗大量的CPU或者内存资源。比如构造一个多层嵌套的XML结构,或者定义几十万个实体让解析器逐个处理,最终导致服务资源耗尽,无法正常响应正常请求。
不同解析方式的安全配置方法
使用DocumentBuilderFactory解析
DocumentBuilderFactory是Java常用的DOM解析方式,默认配置下存在安全风险,需要手动关闭外部实体和DTD处理,具体配置代码如下:
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import java.io.File;
public class SafeXmlParser {
public static Document parseXmlSafe(String xmlPath) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// 关闭外部通用实体
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
// 关闭外部参数实体
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
// 禁用DTD,从根源避免XXE和递归DTD导致的DoS
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// 关闭XInclude处理
dbf.setXIncludeAware(false);
// 关闭扩展实体引用
dbf.setExpandEntityReferences(false);
DocumentBuilder db = dbf.newDocumentBuilder();
return db.parse(xmlPath);
}
}
使用SAXParserFactory解析
SAX解析是事件驱动的流式解析方式,同样需要配置对应的安全特性,避免恶意XML的影响,配置示例如下:
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;
import org.xml.sax.helpers.DefaultHandler;
import java.io.File;
public class SafeSaxParser {
public static void parseXmlSafe(String xmlPath) throws Exception {
SAXParserFactory spf = SAXParserFactory.newInstance();
// 禁用DTD
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);
SAXParser saxParser = spf.newSAXParser();
saxParser.parse(xmlPath, new DefaultHandler());
}
}
使用XMLInputFactory解析(StAX方式)
StAX是Java中另一种常用的XML解析方式,对应的安全配置需要设置XMLInputFactory的相关属性,代码如下:
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.FileInputStream;
public class SafeStaxParser {
public static void parseXmlSafe(String xmlPath) throws Exception {
XMLInputFactory xif = XMLInputFactory.newInstance();
// 禁用外部实体
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
// 禁用DTD
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
XMLStreamReader reader = xif.createXMLStreamReader(new FileInputStream(xmlPath));
while (reader.hasNext()) {
reader.next();
}
reader.close();
}
}
额外的安全防护建议
- 对输入的XML内容做大小限制,避免处理超大XML文件导致的DoS问题,可以在读取前校验文件或者输入流的大小。
- 如果业务不需要使用DTD,优先选择禁用DTD的配置,这是防范XXE和DTD相关DoS最有效的手段。
- 不要信任外部传入的XML内容,所有解析操作都使用统一的安全配置模板,避免部分场景遗漏安全设置。
- 定期对XML解析相关的依赖库做版本升级,修复已知的安全漏洞。
配置验证方法
配置完成后可以使用之前提到的恶意XML示例做测试,如果解析器抛出异常提示禁止DOCTYPE声明或者外部实体加载失败,说明安全配置生效。如果出现返回/etc/passwd文件内容的情况,说明配置没有生效,需要重新检查配置项的拼写和设置顺序。