在进行安全性评估时,我们注意到了标记语言 Edge Side Includes (ESI)中的一个意外行为,这种语言用于许多流行的 HTTP 代理(反向代理、负载平衡器、缓存服务器、代理服务器)。 我们发现成功的 ESI 攻击可以导致服务器端请求伪造(SSRF)、各种绕过 HTTPOnly cookie 缓解标志的跨站脚本向量(XSS)和服务器端分布式拒绝服务攻击。 我们称这种技术为 ESI 注入。
通过我们的测试,我们发现了十几种流行的可以处理 ESI 的产品: Varnish、 Squid Proxy、 IBM WebSphere、 Oracle fusion / weblogic、 Akamai、 Fastly、 F5、 Node.js、 LiteSpeed 和一些特定语言的插件。 并非所有这些都默认启用了 ESI,但下面将进一步讨论这个问题。
什么是Edge Side Includes(ESI) ?
ESI 语言基于了一小部分 XML 标签集合,在许多流行的 HTTP 代理解决方案中使用,通过启用 Web 内容的高速缓存来解决性能问题。 ESI 标签用于指示反向代理(或缓存服务器)获取已缓存模板的网页的更多信息。 这些信息在提供给客户端之前可能来自另一台服务器。 这允许完全缓存的页面包含动态内容。
ESI 的一个常见用例是为那些基本上是静态的页面提供动态数据片段。 ESI 允许开发人员用 ESI 标记替换页面的动态部分,从而增加了缓存灵活性。 因此,在请求页面时,代理将处理并获取 ESI 标记,从而确保后端应用程序服务器的性能。
下面的图片可用于演示 ESI 的一个典型用例,即天气网站将缓存城市天气页面的内容。 然后,动态数据将被它们各自指向 API 端点 URL 的 ESI 标记替换。奇热
通过 ESI 构建网页的演示
ESI 的语法相当简单。 上一个例子的 HTML 文件看起来是这样的:
<body> <b>The Weather Website</b> Weather for <ESI :include src="/weather/name?id=$(QUERY_STRING{city_id})" /> Monday: <ESI :include src="/weather/week/monday?id=$(QUERY_STRING{city_id})" /> Tuesday: <ESI :include src="/weather/week/tuesday?id=$(QUERY_STRING{city_id})" /> […]
最初的 ESI 规范可以追溯到2001年,每个供应商的实现差异很大。 每个产品的特性集是不同的; 一些产品将缺少一些特性,但在其他产品中会出现。 你可以在这里阅读关于原始 ESI 规范的更多信息: http://www.w3.org/tr/ESI-lang。 它描述了标记语言的用法和常用特性。 各种供应商,包括 Akamai 和 Oracle,也在规范之外增加了额外的功能。
问题所在
HTTP 代理不能区分由上游服务器提供的合法 ESI 标记和在 HTTP 响应中注入的恶意标记。 换句话说,如果攻击者能够成功地在 HTTP 响应中反射 ESI 标记,那么代理将盲目地解析和计算它们,相信它们是来自上游服务器的合法标记。
为了让 ESI 解析器处理 ESI 标记,解析器不能对小于号和大于号的字符进行编码或转义。 如今,web 应用服务器通常会避开用户可控的特殊字符,以缓解 XSS 攻击。 虽然这将有效地阻止被代理程序解析的 ESI 标记,但 ESI 标记有时可以被注入到非 HTML 的 HTTP 响应中。 实际上,ESI 的一个现代特性是允许开发人员向缓存和静态数据源添加动态内容,比如 JSON 对象和 CSV。 在 Fastly 的博客上可以找到关于 ESI + JSON 的详尽教程,教程的内容显示可以配置 ESI 解析器来处理 JSON 对象中的 ESI 标记。 由于现代框架会尝试将它们的转义工作置于上下文环境中,所以 API 端点允许 JSON 属性中类似 HTML 的字符串并不罕见,因为它们不应该被浏览器解释为 HTML。 但是,这允许攻击者破坏反射在带有 ESI 标记的 JSON 响应中的输入,代理将在传输过程中对其进行解释。
前面的场景非常罕见,因为它不代表任何已分析的支持 ESI 的产品的默认行为。 大多数常见的攻击向量将由后端服务器反射 ESI 标记,然后由启用 ESI 的负载均衡器或代理进行处理。 显然,如果对用户输入进行了适当的清理(为了减轻 XSS 攻击,应该这样做) ,则代理将对 ESI 标记进行编码,并且永远不会对其进行处理。
ESI 注入的副作用
让我们来看看一些注入的常见场景,以及它们可以用来做什么:
服务器端请求伪造(SSRF)
可以说,ESI 规范最常见和最有用的特性是 include 的使用。 ESI 的 include 是一个标签,当代理或负载均衡器处理这些标记时,执行另一个 HTTP 请求来获