在我的上一篇文章中,我们了解了如何配置XML解析器以全面禁用XXE声明和扩展。这是一个简单但严格的解决方案,可能难以在您的项目中实施。那么今天,我们就来聊一聊如何精确限制XXE。同样,我们的示例代码将主要使用Java代码,同时也会参考其他语言的代码。限制授权协议的外部连接JavaJAXPAPI版本1.5添加了新属性以限制对外部内容的访问。在本文中,我们将只关注限制XXE,从而限制对外部DTD属性的访问。但限制对其他外部资源(例如模式)的访问对于降低其他XML漏洞的风险也是必要的。以下属性必须使用授权协议列表进行定义:FactorysetProperty(xmlcontents.ACCESS_EXTERNAL_DTD,"http");在这个例子中,只允许连接到http资源,防止使用文件URI方案使用XXE过滤包含敏感内容的文件,但这仍然不足以有效避免漏洞,因为敏感内容可以从网络可访问的资源中检索。正如我们稍后将看到的,此功能通常用作第一个过滤器,与实体解析器结合使用。通过将此功能设置为空字符串,可以安全地完全禁用XXE:FactorysetProperty(xmlcontents.ACCESS_EXTERNAL_DTD,");注意:我们了解到某些XML处理器具有一些功能,例如ApacheXerces,它们仍然不支持这些JAXP1。5个属性。对于libxml数据库,限制外部连接最快的设置是libxml_NONET函数,它禁止在检索外部资源时使用网络。这是一个PHP示例:$doc=simplexml_load_string($xml,"simplexmlment",LIBXML_NONET);CustomparserresolveentitiesSAX(SimpleAPIforXML,最初是一种只用Java语言表达的API)解析器是基于事件驱动的API。对于在XML文档中找到的每个实体引用,通过EntityResolver接口实现的resolveEntity是称为回调函数。默认情况下,内置的Java解析器会尝试访问XML文档中定义的几乎所有外部内容。因此,为了保护SAX应用程序的安全,XXE声明或引用扩展应该被禁用,因为我们已经完成了在这个讨论内容的第二篇文章中描述的,或者根据您的应用程序的需要使用自定义解析器。使用自定义解析器的一个常见用例是使用资源的缓存版本而不是从网络中获取它。将URI资源的方案(例如http://)替换为更合适的方案(https://)。将相对URI资源转换为绝对URI资源。只有安全和经过身份验证的实体才能获得授权。自定义解析器由EntityResolver接口的实现组成,应该使用SAX处理器的setEntityResolver方法进行注册。例如,我们在开源项目中看到很多有效的防止XXE漏洞的解决方案,就是关联一个空字符串作为任意实体的内容,可以完全禁用实体解析。与我们之前讨论的解决方案的不同之处在于,这里允许在XML文件中声明XXE,但禁用它们的解析,从而提供了一种非阻塞且安全的解析XML文件的方法:builder.xml文件。setEntityResolver(newEntityResolver(){@OverridepublicInputSourceresolveEntity(StringpublicId,StringsystemId)抛出SAXException,IOException{returnnewInputSource(newStringReader(""));}});请注意,为实体冲突解决程序注册null等同于使用默认且不安全的解析器,因此可能没有充分的理由这样做:builder.setEntityResolver(null);在自定义解析器中,当返回null时,解析器的默认行为是获取外部内容:builder.setEntityResolver(newEntityResolver(){@OverridepublicInputSourceresolveEntity(StringpublicId,StringsystemId)throwsSAXException,IOException{returnnull;}});因此,如上所述的许可实体解析器仍然可能导致XXE漏洞。这就是我们之前讨论的属性发挥作用的地方。entityResolver对systemId以logo.png结尾的实体使用自定义解析,否则使用默认行为。由于通过将ACCESS_EXTERNAL_DTD属性设置为空字符串在第一行修改了默认行为,因此实体解析器是安全的。builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD,"");builder.setEntityResolver(newEntityResolver(){@OverridepublicInputSourceresolveEntity(StringpublicId,StringsystemId)throwsSAXException,IOException{if(systemId.endsWith("logo.png")){InputStreamin=classLoader.getResourceAsStream("com/package/logo.png");returnnewInputSource(newStringReader(Base64.getEncoder().encodeToString(IOUtils.toByteArray(in))));}returnnull;}});其他语言的其他数据库提供类似的功能,允许自定义解析外部实体,例如PHP的libxml和libxml_set_external_entity_loader函数:(./logo.png","r");}返回null;});libxml的主要区别在于,在自定义解析器中返回null实体未被解析并发生错误。使用目录解析实体实体到其他实体的映射通常也使用XML目录来完成。XML目录是包含外部实体标识符到URI的映射的XML文件。
