0x01 起因
同事问我研究过Java下的xxe漏洞嘛,为啥修复建议不起作用,emmmm然后这个问题我就回答不上来了,这个问题有两个关注点:
1.java下xxe产生的原理是啥。
2.修复建议的修复代码是啥。
0x02 深入分析
1.DocumentBuilder
原理分析
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.l1nk3r.xxe.javaxxe; import org.w3c.dom.Document; import javax.xml.parsers.*; import java.io.ByteArrayInputStream; import java.io.InputStream;
public class DocumentXXE { public static void main(String[] args) throws Exception { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); String str = "<!DOCTYPE doc [ \n" + "<!ENTITY xxe SYSTEM \"http://127.0.0.1:8888\">\n" + "]><doc>&xxe;</doc>"; InputStream is = new ByteArrayInputStream(str.getBytes()); Document doc = db.parse(is); } }
|
在db.parse
处下个断点,代码来到这个Java.xml.parsers.DocumentBuilder#parse类中,跟进return parse
。

在DocumentBuilderImpl#parse中,调用了 DOMParser#parse

跟进 DOMParser#parse 这个方法,调用 parse 方法来解析 xmlInputSource 。

跟进 XMLParser#parse ,调用 fConfiguration.parse 方法。

而这个 fConfiguration.parse 实际上是个继承接口,这里根据 debug 会进入XML11Configuration#parse
中。


通过setInputSource(source)
方法将Xml数据赋值给 fInputSource ,然后调用parse(boolean complete)
构造方法进行处理。

跟进parse(boolean complete)
方法,然后就来到了XMLDocumentFragmentScannerImpl#scanDocument
方法中。

跟进XMLDocumentFragmentScannerImpl#scanDocument
方法,这个方法首先会针对xml数据流中的document部分进行扫描。

然后就会扫描xml数据流中的 DTD 部分。

最后开始扫描 ELEMENT 部分。

然后调用next方法。

跟进 XMLDocumentFragmentScannerImpl#next ,这个方法会针对一些特殊字符进行标记。

而我们刚刚payload:<doc>&xxe;</doc>
里面存在&
特殊字符,所以这里会调用 setScannerState 构造方法将fScannerState
设置为 SCANNER_STATE_REFERENCE 。

代码继续往下走,会来到 XMLDocumentFragmentScannerImpl#next 方法中的下图代码位置,我们已经知道这时候的fScannerState
是 SCANNER_STATE_REFERENCE ,所以这里的 case 应该要来到SCANNER_STATE_REFERENCE
中。


在SCANNER_STATE_REFERENCE
这个case中,会调用 scanEntityReference 来处理 fContentBuffer中的数据。跟进 scanEntityReference 方法,首先会获取 scanName ,我们payload里面的name自然是xxe,所以这里debug的结果也是xxe。

代码继续下行,来到下图位置,调用 XMLEntityManager#startEntity 进行处理。

跟进 XMLEntityManager#startEntity 最后会调用startEntity(String name,XMLInputSource xmlInputSource,boolean literal, boolean isExternal)
这个构造方法,并且可以看到已经解析了payload中的外部地址。

在 startEntity 方法中调用了 setupCurrentEntity 方法。

跟进 XMLEntityManager#setupCurrentEntity ,这里可以看到解析了我们payload中的地址,并且发起了http的请求。

实际调用栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| setupCurrentEntity:646, XMLEntityManager (com.sun.org.apache.xerces.internal.impl) startEntity:1300, XMLEntityManager (com.sun.org.apache.xerces.internal.impl) startEntity:1237, XMLEntityManager (com.sun.org.apache.xerces.internal.impl) scanEntityReference:1908, XMLDocumentFragmentScannerImpl (com.sun.org.apache.xerces.internal.impl) next:3067, XMLDocumentFragmentScannerImpl$FragmentContentDriver (com.sun.org.apache.xerces.internal.impl) next:606, XMLDocumentScannerImpl (com.sun.org.apache.xerces.internal.impl) scanDocument:510, XMLDocumentFragmentScannerImpl (com.sun.org.apache.xerces.internal.impl) parse:848, XML11Configuration (com.sun.org.apache.xerces.internal.parsers) parse:777, XML11Configuration (com.sun.org.apache.xerces.internal.parsers) parse:141, XMLParser (com.sun.org.apache.xerces.internal.parsers) parse:243, DOMParser (com.sun.org.apache.xerces.internal.parsers) parse:348, DocumentBuilderImpl (com.sun.org.apache.xerces.internal.jaxp) parse:121, DocumentBuilder (javax.xml.parsers) main:19, DocumentXXE (com.l1nk3r.xxe.javaxxe)
|
代码流程图

漏洞修复
错误的修复方式
目前百度查询xxe的修复方式实际上有这么一段代码,但是这么一段代码实际上是不会生效。

看看为啥不起作用,可以选择在dbf.setExpandEntityReferences(false);
下一个断点。 DocumentBuilderFactoryImpl 这个类中的 expandEntityRef 变量的值默认是true,通过setExpandEntityReferences(false)
之后将 expandEntityRef 变量的值设置为false。

代码继续下行来到 dbf.newDocumentBuilder 中。

该方法会返回一个实例化的 DocumentBuilderFactoryImpl 对象。

而在 DocumentBuilderFactoryImpl 这个对象中,会根据刚刚的 expandEntityRef 的值取反之后赋值给CREATE_ENTITY_REF_NODES_FEATURE,也就是http://apache.org/xml/features/dom/create-entity-ref-nodes
,对应的结果为 true 。

最后这里处理完之后自然返回 DocumentBuilderFactoryImpl 对象,代码继续下行来到parse处。

前面步骤省略,和之前一致,来到 XMLParser#parse 这里之后,这里有个 reset 方法。

跟进这个 reset 方法,实际上来到了 AbstractDOMParser#reset 中,并且将 fCreateEntityRefNodes 的值设置为我们刚刚修改过,也就是 true 这个值。

这是到这部分的调用栈

之后代码继续下行,在XMLDocumentFragmentScannerImpl#scanDocument
方法中,会先扫描 document 部分,再扫描 DTD 部分,最后扫描 ELEMENT 部分。 XMLDocumentFragmentScannerImpl#next 会针对&
进行标记,调用 scanEntityReference 方法将fScannerState
设置为 SCANNER_STATE_REFERENCE ,最后依然会调用 setupCurrentEntity 创建连接并发起请求,以获取外部实体的内容。
然后继续往下走来到 XMLEntityManager#endEntity ,经过一系列调用会来到AbstractDOMParser#endGeneralEntity
中,会判断前面设置的 fCreateEntityRefNodes 的值,如果为 true则展开实体引用到生成的文档中替换掉&xxx
的实体引用声明,设置为false则保留实体引用声明的DOM树在生成的文档中。

此方法作用于XML解析后生成的文档。setExpandEntityReferences设置为true则展开实体引用到生成的文档中替换掉&xx的实体引用声明,setExpandEntityReferences设置为false则保留实体引用声明的DOM树在生成的文档中。

正确的修复方式
1 2 3 4 5 6 7 8 9 10
| DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); /*以下为修复代码*/ //https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#Java //禁用DTDs (doctypes),几乎可以防御所有xml实体攻击 dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); //首选 //如果不能禁用DTDs,可以使用下两项,必须两项同时存在 dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); //防止外部实体POC dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); //防止参数实体POC /*以上为修复代码*/ DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(request.getInputStream());
|
当然如果还不放心的话,下面是owasp推荐的,其实也就是多了三个属性的设置,具体可以看看下面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); String FEATURE = null; try { FEATURE = "http://apache.org/xml/features/disallow-doctype-decl"; dbf.setFeature(FEATURE, true);
FEATURE = "http://xml.org/sax/features/external-general-entities"; dbf.setFeature(FEATURE, false);
FEATURE = "http://xml.org/sax/features/external-parameter-entities"; dbf.setFeature(FEATURE, false);
FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; dbf.setFeature(FEATURE, false);
dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false); >..
DocumentBuilder safebuilder = dbf.newDocumentBuilder();
|
当然还是需要看一下原理,选择在下面这个位置代码下个断点。
1
| dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
|
前面我们在错误修复方法里面,在dbf.setExpandEntityReferences(false);
的时候,我们知道在 DocumentBuilderImpl 类中,调用domParser.setFeature
将 expandEntityRef 取反之后赋值给CREATE_ENTITY_REF_NODES_FEATURE,之后调用 reset 方法的时候将 fCreateEntityRefNodes 的设置为 CREATE_ENTITY_REF_NODES_FEATURE 的结果。
回到现在这个设置,有点不太一样,在 DocumentBuilderImpl 类中找不到我们设置的这个disallow-doctype-decl
的配置,因此会继续向下,下图是在 DocumentBuilderImpl 中第234行,调用 setFeatures 方法。

这个方法会继续向下行,来到了 XMLDocumentScannerImpl#setFeature 中,并且将 fDisallowDoctype 设置为true
,这是整个 setFeature 操作过程中的调用链。

紧接着进入到 XMLDocumentFragmentScannerImpl#scanDocument ,我们知道首先会扫描document
部分,然后调用 XMLDocumentScannerImpl$PrologDriver#next 方法。

在 XMLDocumentScannerImpl$PrologDriver#next 方法中,调用 setScannerState 将 fScannerState 设置为24,也就是 SCANNER_STATE_DOCTYPE 属性。


紧接着代码继续下行,根据 fScannerState 进行选择,这里我们前面的 fScannerState 的状态设置为了 SCANNER_STATE_DOCTYPE ,所以自然进入这个case中,然后针对我们之前的 fDisallowDoctype 属性进行判断,如果是true的话,就抛出异常。

这是到这个部分的调用栈。

异常会被抛到XML11Configuration.parse()
中处理。处理的结果是fParseInProgress
变量被设置为了false
,接着会调用cleanup()
方法在完全解析XML文档之前终止解析,这个是到最后终止部分的调用栈。


2.SAXBuilder
原理分析
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.l1nk3r.xxe.javaxxe;
import java.io.ByteArrayInputStream; import java.io.InputStream; import org.jdom2.Document;d import org.jdom2.input.SAXBuilder;
public class SAXBuilderXxe { public static void main(String[] args) throws Exception { String str = "<!DOCTYPE doc [ \n" + "<!ENTITY xxe SYSTEM \"http://127.0.0.1:8888\">\n" + "]><doc>&xxe;</doc>"; InputStream is = new ByteArrayInputStream(str.getBytes()); SAXBuilder sb = new SAXBuilder(); Document doc = sb.build(is); } }
|
下图是这个方法到触发xxe的调用栈,可以看到画圈的部分和我们前面分析的 DocumentBuilder 中造成xxe的调用栈基本相似,最后都是到 XMLEntityManager#setupCurrentEntity 中触发xxe。

修复建议
修复建议可以参考下列代码,注意实例化 SAXBuilder 类和 build 方法解析之间的这些属性。
1 2 3 4 5 6
| SAXBuilder sb = new SAXBuilder(); sb.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); sb.setFeature("http://xml.org/sax/features/external-general-entities", false); sb.setFeature("http://xml.org/sax/features/external-parameter-entities", false); sb.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); Document doc = sb.build(is);
|
可以看到实际上将 disallow-doctype-decl 设置为true之后,就会抛出以下报错,具体原因和DocumentBuilder
一致。

3.SAXParserFactory
原理分析
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.l1nk3r.xxe.javaxxe;
import org.xml.sax.HandlerBase; import java.io.ByteArrayInputStream; import java.io.InputStream; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory;
public class SAXParseFactoryXxe { public static void main(String[] args) throws Exception { String str = "<!DOCTYPE doc [ \n" + "<!ENTITY xxe SYSTEM \"http://127.0.0.1:8888\">\n" + "]><doc>&xxe;</doc>"; InputStream is = new ByteArrayInputStream(str.getBytes()); SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser parser = spf.newSAXParser(); parser.parse(is, (HandlerBase) null); } }
|
默认情况下也存在xxe,具体看下图调用栈就懂了。

修复建议
1 2 3 4 5 6
| 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); spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); SAXParser parser = spf.newSAXParser();
|
漏洞原理
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.l1nk3r.xxe.javaxxe;
import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.stream.StreamSource; import java.io.ByteArrayInputStream; import java.io.InputStream;
public class SAXTransformerFactoryXxe { public static void main(String[] args) throws Exception { String str = "<!DOCTYPE doc [ \n" + "<!ENTITY xxe SYSTEM \"http://127.0.0.1:8888\">\n" + "]><doc>&xxe;</doc>"; InputStream is = new ByteArrayInputStream(str.getBytes()); SAXTransformerFactory sf = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); StreamSource source = new StreamSource(is); sf.newTransformerHandler(source); } }
|
调用栈:

修复建议
1 2 3 4 5
| SAXTransformerFactory sf = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); StreamSource source = new StreamSource(is); sf.newTransformerHandler(source);
|
5.SAXReader
漏洞原理
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.l1nk3r.xxe.javaxxe;
import java.io.ByteArrayInputStream; import java.io.InputStream; import org.dom4j.io.SAXReader;
public class SAXReaderXxe { public static void main(String[] args) throws Exception { String str = "<!DOCTYPE doc [ \n" + "<!ENTITY xxe SYSTEM \"http://127.0.0.1:8888\">\n" + "]><doc>&xxe;</doc>"; InputStream is = new ByteArrayInputStream(str.getBytes()); SAXReader saxReader = new SAXReader(); saxReader.read(is); } }
|
调用栈

修复建议
1 2 3 4 5 6
| SAXReader saxReader = new SAXReader(); saxReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); saxReader.setFeature("http://xml.org/sax/features/external-general-entities", false); saxReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); saxReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); saxReader.read(is);
|
6.XMLReader
漏洞原理
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.l1nk3r.xxe.javaxxe;
import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory;
import java.io.ByteArrayInputStream; import java.io.InputStream;
public class XMLReaderXxe { public static void main(String[] args) throws Exception { String str = "<!DOCTYPE doc [ \n" + "<!ENTITY xxe SYSTEM \"http://127.0.0.1:8888\">\n" + "]><doc>&xxe;</doc>"; InputStream is = new ByteArrayInputStream(str.getBytes()); XMLReader reader = XMLReaderFactory.createXMLReader(); reader.parse(new InputSource(is)); } }
|
调用栈:

修复建议
1 2 3 4 5 6
| XMLReader reader = XMLReaderFactory.createXMLReader(); reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); reader.setFeature("http://xml.org/sax/features/external-general-entities", false); reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); reader.parse(new InputSource(is));
|
7.SchemaFactory
漏洞原理
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.l1nk3r.xxe.javaxxe;
import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import java.io.ByteArrayInputStream; import java.io.InputStream;
public class SchemaFactoryXxe { public static void main(String[] args) throws Exception { String str = "<!DOCTYPE doc [ \n" + "<!ENTITY xxe SYSTEM \"http://127.0.0.1:8888\">\n" + "]><doc>&xxe;</doc>"; InputStream is = new ByteArrayInputStream(str.getBytes()); SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); StreamSource source = new StreamSource(is); Schema schema = factory.newSchema(source); } }
|
调用栈:

修复建议
1 2 3 4 5
| SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); StreamSource source = new StreamSource(is); Schema schema = factory.newSchema(source);
|
漏洞原理
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package com.l1nk3r.xxe.javaxxe;
import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamReader;
public class XMLInputFactoryXxe { public static void main(String[] args) throws Exception { XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory(); XMLStreamReader reader = xmlInputFactory.createXMLStreamReader(ResourceUtils.getPoc1()); try { while (reader.hasNext()) { int type = reader.next(); if (type == XMLStreamConstants.START_ELEMENT) { System.out.print(reader.getName()); } else if (type == XMLStreamConstants.CHARACTERS) { System.out.println("type" + type); } else if (type == XMLStreamConstants.END_ELEMENT) { System.out.println(reader.getName()); } } reader.close(); } catch (Exception e) { e.printStackTrace(); } } }
|
1 2 3 4 5 6 7 8 9
| package com.l1nk3r.xxe.javaxxe;
import java.io.InputStream;
public class ResourceUtils { public static InputStream getPoc1() { return ResourceUtils.class.getClassLoader().getResourceAsStream("poc1.xml"); } }
|
调用栈:

修复建议
1 2 3 4
| XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory(); xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); XMLStreamReader reader = xmlInputFactory.createXMLStreamReader(ResourceUtils.getPoc1());
|
漏洞原理
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.l1nk3r.xxe.javaxxe;
import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.stream.StreamSource; import java.io.ByteArrayInputStream; import java.io.InputStream;
public class TransformerFactoryXxe { public static void main(String[] args) throws Exception { String str = "<!DOCTYPE doc [ \n" + "<!ENTITY xxe SYSTEM \"http://127.0.0.1:8888\">\n" + "]><doc>&xxe;</doc>"; InputStream is = new ByteArrayInputStream(str.getBytes()); TransformerFactory tf = TransformerFactory.newInstance(); StreamSource source = new StreamSource(is); tf.newTransformer().transform(source, new DOMResult()); } }
|
调用栈:

修复建议
1 2 3 4 5
| TransformerFactory tf = TransformerFactory.newInstance(); tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); StreamSource source = new StreamSource(is); tf.newTransformer().transform(source, new DOMResult());
|
10.Validator
漏洞原理
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.l1nk3r.xxe.javaxxe;
import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import java.io.ByteArrayInputStream; import java.io.InputStream;
public class ValidatorSampleXxe { public static void main(String[] args) throws Exception { String str = "<!DOCTYPE doc [ \n" + "<!ENTITY xxe SYSTEM \"http://127.0.0.1:8888\">\n" + "]><doc>&xxe;</doc>"; InputStream is = new ByteArrayInputStream(str.getBytes()); SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); Schema schema = factory.newSchema(); Validator validator = schema.newValidator(); StreamSource source = new StreamSource(is); validator.validate(source); } }
|
调用栈:

修复建议
1 2 3 4 5 6
| Schema schema = factory.newSchema(); Validator validator = schema.newValidator(); validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); StreamSource source = new StreamSource(is); validator.validate(source);
|
11.Unmarshaller
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.l1nk3r.xxe.javaxxe;
import javax.xml.bind.JAXBContext; import javax.xml.bind.Unmarshaller;
public class UnmarshallerXxe { public static void main(String[] args) throws Exception { Class tClass = UnmarshallerXxe.class; JAXBContext context = JAXBContext.newInstance(tClass); Unmarshaller um = context.createUnmarshaller(); Object o = um.unmarshal(ResourceUtils.getPoc1()); tClass.cast(o); } }
|
使用默认的解析方法不会存在XXE
问题,这也是唯一一个使用默认的解析方法不会存在XXE
的一个库。
0x03 小结
通过上面的总结,默认情况下用 Unmarshaller 来处理xml不会发生xxe的问题。我们可以看到调用栈的过程中,存在xxe问题的库或者类实际上最后底层调用都是jdk自身处理xml的类,最后的核心触发流程都会来到 XMLEntityManager#setupCurrentEntity 当中。
针对修复方式实际上也是两种:
其一是通过setfeature的方式来防御XXE
,主要几个配置项如下所示:
1 2 3 4
| "http://apache.org/xml/features/disallow-doctype-decl", true "http://apache.org/xml/features/nonvalidating/load-external-dtd", false "http://xml.org/sax/features/external-general-entities", false "http://xml.org/sax/features/external-parameter-entities", false
|
还有一种是通过修改 XMLConstants 这个类的一些配置来进行修复:
1 2
| XMLConstants.ACCESS_EXTERNAL_DTD, "" XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""
|
实际上通常禁用DTD,就ok了。但是由于一些实际业务情况,无法禁用DTD,这时候建议禁用通用实体和参数实体一起配置,因为禁用了通用实体就不会被回显,禁用了参数实体就不会被外带查询。
当然xml配置中还有很多冗余部分,具体可以看看前文 DocumentBuilder 中正确修复方式中的一些官方给的配置。
最后当然再提一个小建议,修复XXE建议采取最小化原则。如果一股脑将这些参数全部禁用或者限制,可能会出现一些奇怪的bug。
Reference
Java XXE注入修复问题填坑实录
修不好的洞,JDK的坑——从WxJava XXE注入漏洞中发现了一个对JDK的误会
XML_External_Entity_Prevention_Cheat_Sheet
一个被广泛流传的XXE漏洞错误修复方案
JAVA常见的XXE漏洞写法和防御