在我的上一篇文章中,咱们明确了如何配置 XML 解析器来实现全面禁用 XXE 申明和扩大。这是一个简略但严格的解决方案,可能很难在您的我的项目中实现。所以明天,咱们将探讨如何准确地限度 XXE。同样,咱们的示例代码将次要应用 Java 代码,同时也参考其余语言的代码。
限度内部连贯到受权协定
Java JAXP API 1.5 版增加了新的属性来限度对外部内容的拜访。在本文中,咱们将只关注限度 XXE,从而限度拜访内部 DTD 属性。但限度对其余内部资源(如模式)的拜访对于升高其余 XML 破绽的危险也具备必要性。
必须应用受权协定列表定义以下属性:
Factory setProperty(xmlcontents.ACCESS_EXTERNAL_DTD,“http”);
在本例中,只容许连贯至 http 资源,避免应用文件 URI 计划应用 XXEs 过滤包含敏感内容的文件,但这依然不足以无效防止破绽,因为敏感内容能够从网络可拜访的资源中被检索进去。稍后咱们将会看到,此性能通常用做第一个过滤器,并与实体解析器配合应用。
通过将此性能设置为空字符串,能够平安地齐全禁用 XXE:
Factory setProperty(xmlcontents.ACCESS_EXTERNAL_DTD,”);
注:咱们曾经理解到了一些 XML 处理器具备的性能,比方 ApacheXerces,仍然不反对这些 JAXP1.5 属性。
对于 libxml 数据库,限度内部连贯的最快捷设置是 libxml_NONET 性能,它禁止在检索内部资源时应用网络。以下是一个 PHP 示例:
$doc=simplexml_load_string($xml,“simplexmlement”,LIBXML_NONET);
应用自定义解析程序解析实体
SAX(XML 的简略 API,最后是仅用 Java 语言示意的 API)解析器基于事件驱动的 API。对于在 XML 文档中找到的每个实体援用,都会调用 EntityResolver 接口实现的 resolveEntity 回调函数。在默认的状况下,内置 Java 解析器将会尝试拜访 XML 文档中定义的简直所有内部内容。
因而,为了爱护 SAX 应用程序的平安,应该禁用 XXE 申明或援用扩大,就像咱们在本探讨内容的第二篇文章所述内容一样,或者依据应用程序的须要应用自定义解析器。
应用自定义解析器的常见用例有:
- 应用资源的缓存版本,而非从网络获取资源。
- 将 URI 资源的计划(如 http://)替换为更适合的计划(https://)。
- 将绝对 URI 资源转换为相对 URI 资源。
- 只对平安且通过验证的实体进行受权。
自定义解析器由 EntityResolver 接口实现组成,该实现应应用 SAX 处理器的 setEntityResolver 办法注册。
例如,咱们在开源我的项目中看到过很多避免 XXE 破绽的无效解决方案,就是为任何实体关联一个空字符串作为内容,从而可能齐全禁用实体解析。与咱们之前探讨的解决方案的不同之处在于,这里容许在 XML 文件中应用 XXE 申明,但它们的解析被禁用,因而提供了一种非阻塞且平安的形式来解析 XML 文件:
builder.setEntityResolver(new EntityResolver() {@Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {return new InputSource(new StringReader("")); } });
请留神,将 null 注册为实体抵触解决程序相当于应用默认且不平安的抵触解决程序,因而这样做可能没有很好的理由:
builder.setEntityResolver(null);
在自定义解析器中,当返回 null 时,解析器的默认行为是获取内部内容:
builder.setEntityResolver(new EntityResolver() {@Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {return null;} });
因而,如上所述的已得到许可的实体解析程序仍然可能导致 XXE 破绽。这就是咱们后面探讨的属性要施展的作用所在。entityResolver 对 systemId 以 logo.png 结尾的实体应用自定义解析,否则应用默认行为。因为通过将 ACCESS_EXTERNAL_DTD 属性设置为空字符串已在第一行批改了默认行为,因而实体解析器是平安的。
builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); builder.setEntityResolver(new EntityResolver() {@Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {if (systemId.endsWith("logo.png")) {InputStream in = classLoader.getResourceAsStream("com/package/logo.png"); return new InputSource(new StringReader(Base64.getEncoder().encodeToString(IOUtils.toByteArray(in)))); } return null; } });
其余语言中的其余数据库也提供了相似的性能,容许自定义内部实体的解析,比方 PHP 的 libxml 和 libxml_set_external_entity_loader 函数:
libxml_set_external_entity_loader(function ($public, $system, $context) {if(str_ends_with($system, "logo.png")) {return fopen("./logo.png", "r"); } return null; } );
libxml 的次要区别在于,在自定义解析器中返回 null 不会解析实体,并会导致呈现谬误。
应用目录解析实体
实体到其余实体的映射通常也应用 XML 目录来实现。
XML 目录是蕴含内部实体标识符到 URI 的映射的 XML 文件。
<?xml version="1.0"?> <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog"> <system systemId="https://www.externalwebsite.com/logos/logo.png" uri="logo.png"/> </catalog>
XML 处理器在目录中为解析 XML 文件时找到的每个实体执行查找。如果没有匹配项,则会收回异样提示信息,从而提供一种平安的办法来避免 XXE 破绽。
在 java 中,能够通过调用 setEntityResolver 办法应用目录,如下所示
URL catalogUrl = classLoader.getResource(catalogFile); CatalogResolver cr = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalogUrl.toURI()); builder.setEntityResolver(cr);
无关 XML 破绽的更多信息
只管 XXE 是解析 XML 文件时须要留神的危险性最高的破绽,但也可能会呈现其余问题,例如拒绝服务攻打或应用 xinclude 或 XSLT 文件 I / O 元素获取内部内容。咱们将很快公布一组规定来进一步增强您的 XML 解析器性能。敬请关注!
作者简介:
ERIC THEROND
平安钻研人员