官方防御手册1 https: //cheatsheetseries.owasp.org /cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
DocumentBuilder javax.xml.parsers.DocumentBuilderFactory
禁用外部实体
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 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 ); } catch (Exception e) { ... } DocumentBuilder safebuilder = dbf.newDocumentBuilder();
以上是官方推荐的修复方式,最主要的是第一条feature
1 "http://apache.org/xml/features/disallow-doctype-decl"
也就是XXE攻击失败时经常跳出来的一条语句
以下为个人理解
如果没有特殊需求其实只要开启http://apache.org/xml/features/disallow-doctype-decl
这一条,就可以禁止外部实体加载。
如果还是有加载外部实体的需求,就可以开启如下几条
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 > > 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 ); >
还看到有一种feature,貌似是一种安全的XML加载方式,禁止了http、file等一些协议的加载,具体没深究
1 2 FEATURE = "http://javax.xml.XMLConstants/feature/secure-processing" ; dbf.setFeature(FEATURE, true );
需要注意的是,设置feature是需要在 DocumentBuilder safebuilder = dbf.newDocumentBuilder();
之前
如果在这之后进行操作,还是会存在XXE漏洞。错误示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dbf.newDocumentBuilder(); String FEATURE = null ; FEATURE = "http://javax.xml.XMLConstants/feature/secure-processing" ; dbf.setFeature(FEATURE, true ); FEATURE = "http://apache.org/xml/features/disallow-doctype-decl" ; dbf.setFeature(FEATURE, true ); FEATURE = "http://xml.org/sax/features/external-parameter-entities" ; dbf.setFeature(FEATURE, false ); FEATURE = "http://xml.org/sax/features/external-general-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 ); builder.parse(ResourceUtils.getPoc1());
这样的操作是无效的。
SAXParserFactory & DOM4J 这两个的防御方式也是和DocumentBuilder
一样的
1 2 3 4 5 6 7 8 9 10 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(); parser.parse(ResourceUtils.getPoc1(), (HandlerBase) null );
调用方式也是和之前一样,不能颠倒,先setFeature
再newSAXParser
防御方式
1 2 3 4 5 6 7 8 9 XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory(); xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false ); xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false ); XMLStreamReader parse = xmlInputFactory.createXMLStreamReader(ResourceUtils.getPoc1()); while (parse.hasNext()) { parse.next(); }
当设置了防御参数之后,就不会去解析外部实体的内容
修复方式都类似,都是设置XMLConstants.ACCESS_EXTERNAL_DTD
和XMLConstants.ACCESS_EXTERNAL_SCHEMA
1 2 3 public static final String ACCESS_EXTERNAL_DTD = "http://javax.xml.XMLConstants/property/accessExternalDTD" ;public static final String ACCESS_EXTERNAL_STYLESHEET = "http://javax.xml.XMLConstants/property/accessExternalStylesheet" public static final String ACCESS_EXTERNAL_SCHEMA = "http://javax.xml.XMLConstants/property/accessExternalSchema" ;
修复后的错误提示也都是一致的
每种修复方式都有设置XMLConstants.ACCESS_EXTERNAL_DTD
但XMLConstants.ACCESS_EXTERNAL_STYLESHEET
和XMLConstants.ACCESS_EXTERNAL_SCHEMA
根据不同场景开启
javax.xml.transform.TransformerFactory
这里是ACCESS_EXTERNAL_STYLESHEET
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(ResourceUtils.getPoc1()); tf.newTransformer().transform(source, new DOMResult());
Validator javax.xml.validation.Validator
这里是ACCESS_EXTERNAL_SCHEMA
1 2 3 4 5 6 7 SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema" ); Schema schema = factory.newSchema(); Validator validator = schema.newValidator(); validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "" ); validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "" ); StreamSource source = new StreamSource(ResourceUtils.getPoc1()); validator.validate(source);
SchemaFactory javax.xml.validation.SchemaFactory
这里是ACCESS_EXTERNAL_SCHEMA
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(ResourceUtils.getPoc1()); Schema schema = factory.newSchema(source);
javax.xml.transform.sax.SAXTransformerFactory
这里是ACCESS_EXTERNAL_STYLESHEET
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(ResourceUtils.getPoc1()); sf.newTransformerHandler(source);
XMLReader org.xml.sax.XMLReader
1 2 3 4 5 6 7 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(ResourceUtils.getPoc1()));
和之前的DocumentBuilder
是一样的,假如开启了第一句之后,后面的其实是可以省去的。
SAXReader org.dom4j.io.SAXReader
这里比较特殊,需要将三个防御全部开启
1 2 3 4 5 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.read(ResourceUtils.getPoc1());
在文档中特地标注了这样一句话
Based on testing, if you are missing one of these, you can still be vulnerable to an XXE attack.
如果少了其中一句,还是会存在XXE攻击,至于具体的利用方法,emmm,目前暂未深究。
SAXBuilder org.jdom2.input.SAXBuilder
1 2 3 4 5 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(ResourceUtils.getPoc2());
关于这个,测试的话,仅开启后面两个是没用的。需要将三个选项全部开启 。
JAXB Unmarshaller & XPathExpression 这两个操作都不支持直接的设置禁用外部实体,它不能单独安全地配置,因此必须首先通过另一个安全的XML解析器解析不受信任的数据。
JAXB Unmarshaller javax.xml.bind.Unmarshaller
先生成安全的SAXParserFactory
,再通过它来生成安全的Unmarshaller
1 2 3 4 5 6 7 8 9 10 11 12 SAXParserFactory spf = SAXParserFactory.newInstance(); 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 ); Source xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(), new InputSource(new StringReader(xml))); JAXBContext jc = JAXBContext.newInstance(Object.class ) ; Unmarshaller um = jc.createUnmarshaller(); um.unmarshal(xmlSource);
或者其实这样也是可以的
1 2 3 4 5 6 7 8 9 10 11 SAXParserFactory spf = SAXParserFactory.newInstance(); Source xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(), new InputSource(new StringReader(xml))); xmlReader.setFeature("http://javax.xml.XMLConstants/feature/secure-processing" , true ); xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl" , true ); xmlReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd" , false ); xmlReader.setFeature("http://xml.org/sax/features/external-general-entities" , false ); xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities" , false ); JAXBContext jc = JAXBContext.newInstance(Object.class ) ; Unmarshaller um = jc.createUnmarshaller(); um.unmarshal(xmlSource);
然而其实还存在一种默认的写法,但是不会被日,无需开启防御选项 (感觉正常人应该都是用这种写法的吧)
1 2 3 4 5 Class tClass = Some.class ; JAXBContext context = JAXBContext.newInstance(tClass); Unmarshaller um = context.createUnmarshaller(); Object o = um.unmarshal(ResourceUtils.getPoc1()); tClass.cast(o);
XPathExpression javax.xml.xpath.XPathExpression
,与Unmarshaller
类似
1 2 3 4 5 6 7 8 9 DocumentBuilderFactory df = DocumentBuilderFactory.newInstance(); df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "" ); df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "" ); XPathFactory xpathFactory = XPathFactory.newInstance(); XPath xpath = xpathFactory.newXPath(); XPathExpression expr = xpath.compile("/person" ); DocumentBuilder builder = df.newDocumentBuilder(); String result = expr.evaluate( builder.parse(ResourceUtils.getPoc1()));
XMLDecoder java.beans.XMLDecoder
无解,等死吧,有解的话weblogic也就没那么多事了。
最后 不同的xml解析器的防御方式不尽相同,但大致可以分为如下几种。
也可以根据当前XXE的报错来知道大概是使用了哪种防御方式。
setFeature 1 2 3 4 5 xmlReader.setFeature("http://javax.xml.XMLConstants/feature/secure-processing" , true ); xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl" , true ); xmlReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd" , false ); xmlReader.setFeature("http://xml.org/sax/features/external-general-entities" , false ); xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities" , false );
XMLConstants 1 2 3 df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "" ); df.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "" ); df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "" );
这个比较小众
1 2 xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false ); xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false );
防御策略 由于一些需要开启全部选项才能防御的,而有些只要开启其中一部分选项就能进行防御。
面对这种情况,作为防御方,我们可以采取security default
原则,让开发者尽可能的开启全部防御选项。
至于其他一些第三方库,比如org.apache.poi
这种,则也是在内部调用了SAXReader
这些库,并且没有设置禁用DTD。对于这种只需要升级到最新的版本即可解决。
Reference