对于使用Java原生DOM和SAX以及DOM4J有以下两种修复方法(这里以DocumentBuilderFactory为例):
//仅限
Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl
documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);
2.如果无法完全禁用DTD,可以采用以下操作一起使用
// Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities和http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
// Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities和http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
// JDK7 + - http://xml.org/sax/features/external-general-entities和http://xml.org/sax/features/external-parameter-entities
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities",false);
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
documentBuilderFactory.setXIncludeAware(false);
documentBuilderFactory.setExpandEntityReferences(false);
上述设置功能的方法setFeature也可以换成setAttribute,setAttribute方法用于用户在底层设置特定属性,使用方法和效果是一样的。
上述防御需要Java 7 Update 67,Java 8 Update 20或更高版本,因为DocumentBuilderFactory和SAXParserFactory的上述对策在早期Java版本中被破坏。
也可以从其他类上着手防范xxe漏洞:
如XMLInputFactory、TransformerFactory、Validator、SchemaFactory、SAXTransformerFactory。但这些类都是调用了XMLConstants中的属性,故需要支持JAXP1.5.使用StAX解析xml文件时,可使用下面方法进行防范(这里仅用XMLInputFactory类做为例子:其他类的防护手段类似):
//这将完全禁用该工厂的DTD
xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD,false);
//禁用外部实体
xmlInputFactory.setProperty(“javax.xml.stream.isSupportingExternalEntities”,false);
示例1还有一个解决方案是,定义一个CustomResolver类,这个类实现了org.xml.sax.EntityResolver接口。它可以让SAX应用定制对外部实体的处理。setEntityResolver()方法可以将对应的SAX驱动实例注册进来。这个定制的处理器使用的是一个为外部实体定义的简单的白名单。当输入不是任何指定的、安全地实体源路径时,resolverEntity()方法会返回一个空的InputSource对象。结果是,当解析恶意输入时,这个由自定义的解析器返回的空的InputStream对象会抛出java.net.MalformedURLException异常。需要注意的是,必须创建一个XMLReader对象,以便通过这个对象来设置自定义的实体解析器。
class CustomResolver implements EntityResolver {
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
//check for known good entities
String entityPath = "/home/username/java/xxe/file";
if (systemId.equals(entityPath)) {
System.out.println("Resolving entity: " + publicId + " " + systemId);
return new InputSource(entityPath);
} else {
return new InputSource(); // Disallow unknown entities by returning a blank path
}
}
}
class XXE {
private static void receiveXMLStream(InputStream inStream,DefaultHandler defaultHandler)
throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = null;
try {
saxParser = factory.newSAXParser();
XMLReader reader = saxParser.getXMLReader();
reader.setEntityResolver(new CustomResolver());
reader.setErrorHandler(new DefaultHandler());
InputSource is = new InputSource(inStream);
reader.parse(is);
...
} catch (ParserConfigurationException e) {
...
} catch (SAXException e) {
...
} catch (IOException e) {
...
}
}
...
}