一、从SOP到CORS的演进
先从经典问题说起。在Web安全领域,同源策略(SOP)始终是基础规则:一个域下的文档或脚本,无法随意读取或修改另一个域的文档属性。换言之,Ajax请求默认不允许跨域。这给开发者带来诸多挑战,因此催生了多种跨域方案,如JSONP、Flash Socket等,形态各异。下图可直观展现当时的情形:

后来,CORS(跨源资源共享)应运而生。它定义了一套浏览器与服务器之间的交互规则,用以判断某个跨域请求是否被允许。可视为一种“折中”方案——比完全放开更安全,又比SOP的严格限制更灵活。简单说,CORS旨在让Ajax实现可控的跨域访问,示意如下:

目前W3C的官方文档仍处于工作草案阶段,但正向正式推荐标准推进。好消息是,大多数现代浏览器已提供支持。
服务器端对CORS的支持,主要依赖设置Access-Control-Allow-Origin这个HTTP头。若浏览器检测到相应配置,即可允许Ajax进行跨域访问。举例:
Access-Control-Allow-Origin: https://blog.csdn.net
目前应用CORS的系统包括Face.com、Google Cloud Storage API等,主要用于开放平台向第三方提供访问能力。
二、CORS引发的安全隐患
CORS确实很有用,能共享不少内容,但其风险不容忽视。因为它本质上是一个“盲目”的协议——仅靠HTTP头控制,而HTTP头是可以伪造的。
1. HTTP头无法验证请求来源的真实性
HTTP头只能说明请求来自某个域,但无法证明这个事实。因此,未经身份验证的跨域请求永远不应被信任。如果某些重要功能需要暴露或返回敏感信息,必须验证Session ID。
2. 第三方可能被入侵
设想一个场景:FriendFeed通过跨域请求访问Twitter,FriendFeed获取tweets、提交tweets并执行用户操作,Twitter提供响应。两者相互信任,FriendFeed不验证数据有效性,Twitter也对FriendFeed开放了大部分功能。
但如果Twitter被入侵:
FriendFeed总是从Twitter获取数据,未经过编码或验证就直接显示在页面上。一旦Twitter被入侵,这些数据就可能存在危害。
反过来,如果FriendFeed被入侵:
Twitter响应FriendFeed的请求,比如发表tweets、更换用户名甚至删除账户。攻击者可利用这些请求篡改用户数据。
因此,请求方必须验证接收数据的有效性,服务方则只暴露最少、最必要的功能,这一点至关重要。
3. 恶意跨域请求
即便页面只允许来自某个信任域的请求,它仍然会收到大量其他域的跨域请求。这些请求若被用于执行应用层的DDOS攻击,后果严重。
举例:考虑一个搜索页面,当通过%参数请求时,搜索服务器会返回所有记录——这是一个计算密集型操作。要击垮这个网站,攻击者可利用XSS漏洞将JavaScript脚本注入某个公共论坛,当用户访问该论坛时,其浏览器会重复执行针对该搜索服务器的请求。或者更简单,使用一个目标地址包含请求参数的图像元素也能达到同样目的。如果可能,攻击者甚至可以创建一个Web Worker来执行这种攻击,大量消耗服务器资源。
有效的解决办法是通过多种条件(如HTTP头、参数等)来屏蔽非法请求。
4. 内部信息泄露
假设一个内部站点开启了CORS。如果内部网络的用户访问了恶意网站,恶意网站可通过跨域请求获取内部站点的内容,带来信息泄露风险。
5. 针对用户的攻击
前面几个风险主要针对服务器,这一条则针对用户。比如,攻击者发现某个名为productsearch.php的页面存在SQL注入漏洞,且该页面可全域访问。攻击者并不直接动手,而是编写一个JavaScript数据采集脚本,插入到自己的网站或存在XSS漏洞的网站上。当受害者访问这个恶意页面时,浏览器就会自动执行针对productsearch.php的SQL注入攻击,采集所有数据并发送给攻击者。服务器日志显示,攻击来自受害者的IP,而受害者自己的系统并未被攻破,没有任何恶意软件痕迹。
三、攻击工具
Shell of the Future是一个反向WebShell处理器,它利用HTML5的跨站请求来劫持会话。下面是一个示意界面:

四、防御策略
综上所述,防御CORS攻击需要把握几个关键点:
- 不信任未经身份验证的跨域请求,务必先验证Session ID或Cookie。
- 请求方要验证接收数据的有效性,服务方只暴露最少、最必要的功能。
- 通过多种条件(如HTTP头、参数等)屏蔽非法请求。
以上所述就是CORS攻击的几个关键风险点,理解这些之后,才能更有针对性地做好防护。
