CORS全称为Cross-Origin Resource Sharing即跨域资源共享,用于绕过SOP(同源策略)来实现跨域资源访问的一种技术。而CORS漏洞则是利用CORS技术窃取用户敏感数据。以往与CORS漏洞类似的JSONP劫持虽然已经出现了很多年,但由于部分厂商对此不够重视导致其仍在不断发展和扩散。
美创安全实验室近期监控到全国各地类似于JSONP劫持或CORS漏洞引发的CSRF防御崩溃案例不断增多故在此单独出一篇针对CORS漏洞原理及防御方法的文章,希望看官们好好保护自己的系统免受此种攻击威胁。
对CORS的介绍要从浏览器的同源策略开始说起,SOP全称为Same Origin Policy即同源策略,该策略是浏览器的一个安全基石,同源策略规定:不同域的客户端脚本在没有明确授权的情况下,不能读写对方的资源。简单来说同源策略就是浏览器会阻止一个源与另一个源的资源交互。可以试想一下,如果没有同源策略,当你访问一个正常网站的时候又无意间打开了另一个恶意网站,恶意网站会从你刚刚访问的正常网站上窃取你全部的信息。
SOP是一个很好的策略,在SOP被提出的时期,大家都默默地遵守着这个规定,但随着WEB应用的发展,有些网站由于自身业务的需求,需要实现一些跨域的功能能够让不同域的页面之间能够相互访问各自页面的内容。为了实现这个跨域需求,聪明的程序员想到了一种编码技术JSONP,该技术利用从客户端传入的json格式的返回值,在服务器段调用该接口处事先以定义函数的方式定义好json格式里参数值,并加载script标签调用该函数实现跨域。由此可见JSONP虽然好但他并非是在协议层面解决跨域问题,所以出现了很多安全问题。为了能更安全的进行跨域资源访问,CORS诞生了。
CORS是H5提供的一种机制,WEB应用程序可以通过在HTTP报文中增加特定字段来告诉浏览器,哪些不同来源的服务器是有权访问本站资源。
浏览器将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漏洞的成因很明显,就是服务端配置的规则不当所导致的。
●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
CORS test
当用户点开此网页时,由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漏洞进行跨域资源访问。
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