漫谈同源策略(SOP)和跨域资源共享(CORS)

漫谈同源策略(SOP)和跨域资源共享(CORS),第1张

前言提要:

​ 面试的时候被问到了是否了解同源策略,并没有了解过 (虽然朋友洋写了文章,但是我当时也没有仔细琢磨)。所以有了这篇文…

​ 据说了解同源策略是十分有必要的,要深入了解XSS/CSRF等web安全漏洞,不了解同源策略如同盲人摸象一般,无法说出全貌,更无法应用其进行打击。所以我之前一直在盲人摸象…

一、同源策略(SOP) 什么是源?

答:源就是主机、协议、端口名的一个三元组;同源策略就是同协议、同端口和同主机的一个三同元组

例如:基础知识(HTTP的默认开放端口为80、HTTPS的默认开放端口443)

源的继承

在页面中通过 about:blankjavascript: 这样的伪URL 执行的脚本会继承打开该 URL 的文档的源,因为这些类型的 URL没有包含源服务器的相关信息,数据完全来自客户端。

例如:about:blank 通常作为父脚本写入内容的新的空白d出窗口的 URL(例如,通过 Window.open() )。由完全不同的网站创建的所有about:blank文档就都属于同源页面,如果环境适宜,就可以相互通信而完全不受限制了。

IE中的特例

Internet Explorer 的同源策略有两个主要的差异点:

  • 授信范围(Trust Zones):两个相互之间高度互信的域名,如公司域名(corporate domains),则不受同源策略限制。
  • 端口:IE 未将端口号纳入到同源策略的检查中,因此 https://company.com:81/index.htmlhttps://company.com/index.html 属于同源并且不受任何限制。

这些差异点是不规范的,其它浏览器也未做出支持,但会助于开发基于window RT IE的应用程序。

什么是同源策略(详细)?

答:同源策略(Same Origin Policy, SOP)是Web应用程序的一种安全模型,它保证了不同源(Origin,包括域名,端口和协议类型)的 Web 应用之间不能互相干扰。

​ 同源策略主要是限制了页面最后的那个脚本从另一个源加载资源时的行为,这对于防范恶意页面是一种很好的防御机制,如果恶意脚本请求了非同源的一个东西,那么这种行为就很可能因为同源策略的限制被浏览器拒绝,从而在某种程度上缓解了攻击。

​ 在SOP 的限制下,客户端脚本可以通过资源引用或者跨域表单提交向第三方服务器发送GET请求和POST请求,但是却不能读取响应内容。例如,在图1中,a.com网站脚本可以向 b.com 服务器发送 GET 请求,但是浏览器SOP会阻止其读取响应内容。

为什么要有同源策略?

答:同源策略是一个重要的安全基石,它的目的是为了保证用户信息的安全,防止恶意网址窃取数据。

​ 设想一下如果没有同源策略将导致的安全风险:

​ 假设用户在访问银行网站,并且没有登出。然后他又去了任意的其他网站,刚好这个网站有恶意的js代码,在后台请求银行网站的信息。因为用户目前仍然是银行站点的登陆状态,那么恶意代码就可以在银行站点做任意事情。例如,获取你的最近交易记录,创建一个新的交易等等。因为浏览器可以发送接收银行站点的session cookies,在银行站点域里。访问恶意站点的用户希望他访问的站点没有权限访问银行站点的cookie。当然确实是这样的,js不能直接获取银行站点的session cookie,但是他仍然可以向银行站点发送接收附带银行站点session cookie的请求,本质上就像一个正常用户访问银行站点一样。关于发送的新交易,甚至银行站点的CSRF(跨站请求伪造)防护都无能无力,因为脚本可以轻易的实现正常用户一样的行为。所以如果你需要session或者需要登陆时,所有网站都面临这个问题。

同源策略到底限制了什么?

答:同源策略只作用于实现了同源策略的WEB客户端上,它限制了一个源上的脚本访问来自另一个源的数据。

我们会发现SOP其实在防止CSRF上的作用非常有限,CSRF的请求往往在发送出去的一瞬间就达到了攻击的目的比如发送了一段敏感数据或者请求了一个具体的功能,是否能读取回复其实并不是那么重要(唯一的作用是可以防止CSRF请求读取异常源的授权Token),另外一般静态资源通常不受同源策略的限制,比如png/jpg/css/js等

跨域资源共享(CORS)

在某些情况下同源策略太严格了,给拥有多个子域的大型网站带来问题。如果非同源,共有三种行为受到限制

  • Cookie、LocalStorage 和 IndexDB 无法读取。

  • DOM 无法获得。

  • AJAX 请求不能发送。

虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。下面,我将详细介绍,如何规避上面三种限制的技术方法:跨域资源共享(CSRS)

CORS是什么?

答:跨来源资源共享(Cross-Origin Resource Sharing(CORS))是使用额外HTTP标头来让目前浏览网站的user agent能获得访问不同来源(网域)服务器特定资源之权限的一种浏览器机制。

​ CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

​ 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

​ 因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

CORS的工作原理?

答:CORS 的基本原理是,第三方网站服务器生成访问控制策略,指导用户浏览器放宽 SOP 的限制,实现与指定的目标网站共享数据。具体工作流程可分为三步,如图2所示:

  1. 请求方脚本从用户浏览器发送跨域请求。浏览器会自动在每个跨域请求中添加Origin头,用于声明请求方的源。
  2. 资源服务器根据请求中Origin头返回访问控制策略(Access-Control-Allow-Origin响应头),并在其中声明允许读取响应内容的源。
  3. 浏览器检查资源服务器在Access-Control-Allow-Origin头中声明的源,是否与请求方的源相符,如果相符合,则允许请求方脚本读取响应内容,否则不允许。

简单请求基本流程

浏览器将CORS的请求分成俩类:简单请求和非简单请求

对于简单请求,浏览器直接发出CORS请求。具体来说就是在头信息之中,增加一个Origin字段

例如:

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequestonerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头。

(1)Access-Control-Allow-Origin

该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

(2)Access-Control-Allow-Credentials

该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

(3)Access-Control-Expose-Headers

该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。

withcredentals属性

CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。

Access-Control-Allow-Credentials: true

另一方面,开发者必须在AJAX请求中打开withCredentials属性。

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。

但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials

xhr.withCredentials = false;

需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

非简单请求

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUTDELETE,或者Content-Type字段的类型是application/json

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

上面代码中,HTTP请求的方法是PUT,并且发送一个自定义头信息X-Custom-Header

浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息。

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

除了Origin字段,"预检"请求的头信息包括两个特殊字段。

(1)Access-Control-Request-Method

该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT

(2)Access-Control-Request-Headers

该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header

CORS漏洞

​ CORS的漏洞主要看当我们发起的请求中带有Origin头部字段时,服务器的返回包带有CORS的相关字段并且允许Origin的域访问。

​ 一般测试WEB漏洞都会用上BurpSuite,而BurpSuite可以实现帮助我们检测这个漏洞。
首先是自动在HTTP请求包中加上Origin的头部字段,打开BurpSuite,选择Proxy模块中的Options选项,找到Match and Replace这一栏,勾选Request header 将空替换为Origin:example.com的Enable框。

CORS漏洞测试

当我们进行测试时,看服务器响应头字段里可以关注这几个点:
最好利用的配置:

 Access-Control-Allow-Origin: https://test.com
 Access-Control-Allow-Credentials: true

可能存在可利用的配置:

 Access-Control-Allow-Origin: null
 Access-Control-Allow-Credentials: true

很好的条件但无法利用:

下面这组配置组合虽然看起来很完美但是CORS机制已经默认自动禁止了这种组合,算是CORS的最后一道防线
 Access-Control-Allow-Origin: *
 Access-Control-Allow-Credentials: true

单一的情况:

 Access-Control-Allow-Origin:*
漏洞产生原因

其他可能利用漏洞的地方 解析Origin头时出错

一些支持从多个来源进行访问的应用程序通过使用允许的来源白名单来实现。收到CORS请求后,会将提供的来源与白名单进行比较。如果来源出现在白名单中,那么它会反映在Access-Control-Allow-Origin标题中,以便授予访问权限。例如,web应用收到一个正常的请求:

GET /data HTTP/1.1
Host: bar.com
...
Origin: https://example.com

web应用根据其允许的来源列表检查当前请求资源的来源,如果在列表中,则按以下方式反映该来源:

HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://example.com

但在检测来源是否存在于白名单时经常可能出现问题,一些网站可能会允许其所有的子域(包括尚未存在未来可能存在的子域)来进行访问,或者允许其他网站的域以及其子域来访问请求。这些请求一般都通过通配符或者正则表达式来完成,但是如果这其中出现错误可能就会导致给予其他未被授权的域访问权限。例如:
例如,假设一个应用程序授予对以下列结尾的所有域的访问权限:

 examplecom

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

 exeexample.com

或者,假设应用程序授予对所有以example.com开头的域访问权限,攻击者就可以使用该域获得访问权限:

 example.com.evil-user.net
利用相互受CORS信任的域来进行XSS

假如两个互相受信任的源,如果其中一个网站存在XSS,攻击者就可以利用XSS注入一些JavaScript代码,利用这些代码对信任其源的另一个网站进行敏感信息的获取。
如果进行CORS请求时网站响应:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://vulnerable.com
Access-Control-Allow-Credentials: true

就可以利用XSS漏洞在vulnerable.com网站上使用下面的URL来通过检索API密钥:
https://vulnerable.com/?xss=

白名单中的null值

CORS协议的一个重要安全前提是跨域请求中的Origin头不能被伪造,这个前提并不是总是成立。Origin头最早被提出用于防御CSRF攻击,它的语法格式在RFC 6564中被定义。RFC 6564规定,如果请求来自隐私敏感上下文时,Origin头的值应该为null,但是它却没有明确界定什么是隐私敏感上下文。

CORS协议复用了Origin头,但在CORS标准中同样缺乏对跨域请求Origin中null明确的定义和限制。有些开发者在网站上配置信任 null,用于与本地file页面共享数据,如下所示:

 Access-Control-Allow-Origin: null
 Access-Control-Allow-Credentials: true

在这种情况下,攻击者可以使用各种技巧来生成跨域请求,该请求构造的Origin为null值。这将满足白名单的要求,从而导致跨域访问。例如,可以使用iframe以下格式的沙盒跨域请求来完成:


这就意味着任何配置有Access-Control-Allow-Origin: nullAccess-Control-Allow-Credentials:true的网站等同于没有浏览器SOP的保护,都可以被其他任意域以这种方式读取内容。

CORS漏洞示例

使用DoraBox进行测试

使用CORS跨域资源读取,如图所示会读取到user的信息

使用burpsuite抓取数据包如下图所示,可以看到CORS跨域请求返回的字段

检测的话,只需要在HTTP请求数据包中添加一个字段:Origin: https://test.com(其中https://test.com可以随意设置),只要返回的数据包内容是:Access-Control-Allow-Origin: https://test.com,就说明存在CORS漏洞

利用的话,可以使用CORS的POC,点击Exploit,就会窃取用户的个人信息

POC内容如下,只需要把URL换成存在漏洞的URL即可

DOCTYPE html>
<html>
<body>
<center>
<h2>CORS POC Exploith2>
<h3>Extract SIDh3>
 
<div id="demo">
<button type="button" onclick="cors()">Exploitbutton>
div>
 
<script>
function cors() {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("demo").innerHTML = alert(this.responseText);
    }
  };
  xhttp.open("GET", "http://xxx.xxx.xxx.xxx/DoraBox/csrf/userinfo.php", true);
  xhttp.withCredentials = true;
  xhttp.send();
}
script>
 
body>
html>
CORS漏洞的自动化扫描

CORScanner 是一个 python 工具,旨在发现网站的 CORS 错误配置漏洞。 它可以帮助网站管理员和渗透测试人员检查他们所针对的域/url 是否具有不安全的 CORS 策略。

项目地址:https://github.com/chenjj/CORScanner

下载这个工具
git clone https://github.com/chenjj/CORScanner.git
安装依赖项:
sudo pip install -r requirements.txt

预防CORS漏洞

CORS漏洞主要是由于配置错误而引起的。所以,预防漏洞变成了一个配置问题。下面介绍了一些针对CORS攻击的有效防御措施。

  1. 正确配置跨域请求
    如果Web资源包含敏感信息,则应在Access-Control-Allow-Origin标头中正确指定来源。
  2. 只允许信任的网站
    看起来似乎很明显,但是Access-Control-Allow-Origin中指定的来源只能是受信任的站点。特别是,使用通配符来表示允许的跨域请求的来源而不进行验证很容易被利用,应该避免。
  3. 避免将null列入白名单
    避免使用标题Access-Control-Allow-Origin: null。来自内部文档和沙盒请求的跨域资源调用可以指定null来源。应针对私有和公共服务器的可信来源正确定义CORS头。
  4. 避免在内部网络中使用通配符
    避免在内部网络中使用通配符。当内部浏览器可以访问不受信任的外部域时,仅靠信任网络配置来保护内部资源是不够的。
  5. CORS不能替代服务器端安全策略
    CORS定义了浏览器的行为,绝不能替代服务器端对敏感数据的保护-攻击者可以直接从任何可信来源伪造请求。因此,除了正确配置的CORS之外,Web服务器还应继续对敏感数据应用保护,例如身份验证和会话管理。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/759175.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-01
下一篇 2022-05-01

发表评论

登录后才能评论

评论列表(0条)

保存