Java XXE 防护实战:常见漏洞场景与防御代码全收录

Java XXE 防护实战:常见漏洞场景与防御代码全收录

原创 火力猫 季升安全 2025-04-18 13:50

🛡️ Java XXE 防护示例与详解

本文件涵盖常见 Java XML 解析器的 XXE 安全配置方法,适用学习和辅助判断审计目标是否存在XXE问题:
– 💥 默认行为

  • ⚠️ 潜在风险

  • ✅ 防护方式

  • 👨‍💻 防护代码

📘 1. DocumentBuilderFactory

💥 默认行为:

允许 DTD、实体引用,容易触发 XXE 注入。

⚠️ 潜在风险:

攻击者可构造外部实体,引发敏感信息读取、SSRF、DoS 等攻击。

✅ 防护方式:

通过 setFeature()
 禁用 DTD 和实体加载。

👨‍💻 防护代码:

import javax.xml.parsers.DocumentBuilderFactory;import org.w3c.dom.Document;publicclassSafeDOMParser{publicstaticvoidmain(String[] args)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);        Document doc = factory.newDocumentBuilder().parse(new java.io.ByteArrayInputStream("<xml></xml>".getBytes()));    }}

📘 2. SAXParserFactory

💥 默认行为:

和 DOM 类似,也允许实体展开。

⚠️ 潜在风险:

构造 DTD 搭配实体即可引发漏洞。

✅ 防护方式:

通过 setFeature()
 禁用危险功能。

👨‍💻 防护代码:

import javax.xml.parsers.SAXParserFactory;import org.xml.sax.helpers.DefaultHandler;publicclassSafeSAXParser{publicstaticvoidmain(String[] args)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.newSAXParser().parse(new java.io.ByteArrayInputStream("<xml/>".getBytes()), new DefaultHandler());    }}

📘 3. XMLReader

💥 默认行为:

不设置安全特性即有漏洞风险。

⚠️ 潜在风险:

使用不安全 XMLReader 导致的实体解析。

✅ 防护方式:

直接设置 SAX feature。

👨‍💻 防护代码:

import org.xml.sax.XMLReader;import org.xml.sax.helpers.XMLReaderFactory;import org.xml.sax.helpers.DefaultHandler;publicclassSafeXMLReader{publicstaticvoidmain(String[] args)throws Exception {        XMLReader reader = XMLReaderFactory.createXMLReader();        reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);        reader.setFeature("http://xml.org/sax/features/external-general-entities", false);        reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);        reader.setContentHandler(new DefaultHandler());        reader.parse(new org.xml.sax.InputSource(new java.io.StringReader("<xml/>")));    }}

📘 4. XMLInputFactory(StAX)

💥 默认行为:

开启 DTD 和实体引用。

⚠️ 潜在风险:

即便没有 DTD,也可能加载实体。

✅ 防护方式:

通过 setProperty()
 禁用 DTD、实体支持。

👨‍💻 防护代码:

import javax.xml.stream.*;publicclassSafeStAX{publicstaticvoidmain(String[] args)throws Exception {        XMLInputFactory factory = XMLInputFactory.newInstance();        factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);        factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);        XMLStreamReader reader = factory.createXMLStreamReader(new java.io.StringReader("<xml></xml>"));while (reader.hasNext()) {            reader.next();        }    }}

📘 5. SAXReader(dom4j)

💥 默认行为:

内部创建未配置的 XMLReader。

⚠️ 潜在风险:

默认使用不安全 reader,容易产生 XXE。

✅ 防护方式:

显式创建并配置 XMLReader,然后传入构造器。

👨‍💻 防护代码:

import org.dom4j.io.SAXReader;import org.xml.sax.XMLReader;import org.xml.sax.helpers.XMLReaderFactory;publicclassSafeSAXReader{publicstaticvoidmain(String[] args)throws Exception {        XMLReader xmlReader = XMLReaderFactory.createXMLReader();        xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);        xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);        xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);        SAXReader reader = new SAXReader(xmlReader);        org.dom4j.Document doc = reader.read(new java.io.StringReader("<xml/>"));    }}

📘 6. SAXBuilder(JDOM)

💥 默认行为:

若不指定 XMLReader,也使用不安全解析器。

⚠️ 潜在风险:

默认构造器调用未加固 parser。

✅ 防护方式:

传入经过配置的 XMLReader 实例。

👨‍💻 防护代码:

import org.jdom2.input.SAXBuilder;import javax.xml.parsers.SAXParserFactory;import org.xml.sax.XMLReader;publicclassSafeSAXBuilder{publicstaticvoidmain(String[] args)throws Exception {        SAXParserFactory factory = SAXParserFactory.newInstance();        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/disallow-doctype-decl", true);        XMLReader xmlReader = factory.newSAXParser().getXMLReader();        SAXBuilder builder = new SAXBuilder(xmlReader);        org.jdom2.Document doc = builder.build(new java.io.StringReader("<xml/>"));    }}

📘 7. XStream

💥 默认行为(旧版):

可读取任意类型、支持外部实体。

⚠️ 潜在风险:

反序列化任意对象、文件读取。

✅ 防护方式:

  • 使用 DomDriver

  • 限制允许类型。

👨‍💻 防护代码:

import com.thoughtworks.xstream.XStream;import com.thoughtworks.xstream.io.xml.DomDriver;publicclassSafeXStream{publicstaticvoidmain(String[] args){        XStream xstream = new XStream(new DomDriver());        xstream.allowTypes(new Class[]{MyBean.class});        MyBean obj = new MyBean("hello");        String xml = xstream.toXML(obj);        System.out.println(xml);    }staticclassMyBean{private String name;publicMyBean(String name){ this.name = name; }    }}

📘 8. JAXB

💥 默认行为:

若不指定安全 parser,底层仍可能存在 XXE。

⚠️ 潜在风险:

依赖默认 factory 引发漏洞。

✅ 防护方式:

配合 DOMFactory 设置特性后再解析。

👨‍💻 防护代码:

import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.bind.*;import org.w3c.dom.Document;publicclassSafeJAXB{publicstaticvoidmain(String[] args)throws Exception {        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();        dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);        dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);        dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);        Document doc = dbf.newDocumentBuilder().parse(new java.io.ByteArrayInputStream("<user><name>xx</name></user>".getBytes()));        JAXBContext jc = JAXBContext.newInstance(User.class);        Unmarshaller unmarshaller = jc.createUnmarshaller();        User user = (User) unmarshaller.unmarshal(doc);        System.out.println(user.name);    }staticclassUser{public String name;    }}