理解同源策略总结

0x01 背景

  • 当我们可能观察到了不少网站的一些接口信息。发现浏览器为了保证用户隐私以及其它因素考虑,对于网络接口的调用有一层屏障,这层屏障称为同源策略。那么今天我们就来学习下这个同源策略,希望能提升大家对于接口的测试和利用水平。

0x02 首先说说为什么存在同源策略

  • 我们知道JavaScript可以操作html,可以发出请求,也可以用iframe加载别的网站。那么试想一下,你登陆了一个购物网站比如某宝,接着去访问了VK’Blog。如果VK’Blog利用JavaScript给某宝的收货地址url发起了请求,从原则上讲,这个请求不会成功,否则你的隐私就泄露了。那么控制这个请求的成功与否,就叫同源策略。

  • 总之,同源策略的规定可以概括成:不同域的客户端脚本在没明确授权的情况下,不能读写对方的资源。

0x03 举个例子

  • 假设有以下页面,比较 A 页面与其它页面是否同源~

    1
    2
    3
    4
    5
    A:http://vk.com/a.html 
    B:http://vk.com/b.html
    C:https://vk.com /c.html
    D:http://test.vk.com/d.html
    E:http://vk.com:8081/e.html
  • 根据定义,可以知道 A 和 B 同源,而 A 和 C、D、E 不同源。A、B 页面同源是因为其协议(都是 http)、域名(都是 xys.ttsy)和端口(都是 80)都相同;而 A 与 C、D、E 不同源,是因为 A 和 C 不同协议(http 和 https),A 和 D 不同域名(vk.com 和 test.vk.com),A 和 E 不同端口(80 和 8081) 。

  • 注意

    • 同源策略要求三同, 即: 同域, 同协议, 同端口.
    • 同域即host相同, 顶级域名, 一级域名, 二级域名, 三级域名等必须相同, 且域名不能与 ip 对应;
      • 顶级域名:.cn
      • 一级域名:vk.cn
      • 二级域名:www.vk.cn
      • 三级域名:xxx.www.vk.cn
    • 同协议要求, http与https协议必须保持一致;
    • 同端口要求, 端口号必须相同.
    • IE有些例外, 它仅仅只是验证主机名以及访问协议,而忽略了端口号.
    • 这里需要澄清一个概念, 所谓的域, 跟 js 等资源的存放服务器没有关系, 比如你到 baidu.com 使用 script 标签请求了 google.com 下的js, 那么该 js 所在域是 baidu.com, 而不是 google.com. 换言之, 它能操作baidu.com的页面对象, 却不能操作google.com的页面对象.

0x04 同源策略的限制范围

  • 同源策略下的web世界, 域的壁垒高筑, 从而保证各个网页相互独立, 互相之间不能直接访问, iframe, ajax 等均受其限制, 但是有三个标签是允许跨域加载资源的。

  • Iframe限制

    • 可以访问同域资源, 可读写;
    • 访问跨域页面时, 只读.
  • Ajax限制

    • Ajax 的限制比 iframe 限制更严.
    • 同域资源可读写;
    • 跨域请求会直接被浏览器拦截.(chrome下跨域请求不会发起, 其他浏览器一般是可发送跨域请求, 但响应被浏览器拦截)
  • Cookie限制

    • 只有同源的网页才能共享

  • LocalStorage、IndexDB等存储性内容

  • DOM节点

0x05 允许跨域加载的三个标签

1
2
3
<img src="" />
<link href="" />
<script src=""></script>
  • script并无跨域限制, 这是因为script标签引入的文件不能够被客户端的 js 获取到, 不会影响到原页面的安全, 因此script标签引入的文件没必要遵循浏览器的同源策略. 相反, ajax 加载的文件内容可被客户端 js 获取到, 引入的文件内容可能会泄漏或者影响原页面安全, 故, ajax必须遵循同源策略.

0x06 绕过跨域

  • SOP 带来安全,同时也会带来一定程度的麻烦,因为有时候就是有跨域的需求。绕过跨域的方案由于篇幅所限,并且网上也很多相关文章,所以不在这里展开解决跨域的方案,只给出几个关键词:

  • 对于 ajax

    • 使用 JSONP

      • jsonp是将请求通过动态创建一个“script”标签的方式来将请求发送出去的,所以不是XMLhttpRequest请求,浏览器就不会进行拦截校验。

      • AJAX的使用与平常无异,只需要将dataType改为jsonp即可

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        var result; 
        $.ajax({
        url: 'http://restapi.amap.com/v3/direction/driving?origin=116.45925,39.910031&destination=116.587922,40.081577&output=json&key=5f5b33e5a55685fac26237601cd58a49',
        dataType: "jsonp",
        jsonp: "callback", //与服务端约定的函数名
        cache:true, //是否需要缓存,如果这里没有配置缓存,那么请求的URL还会有一个参数
        success: function(json){
        result = json;
        }
        });
      • 服务端接收到以后,只需要手动判断一下有无callback再手动拼一对括号即可,这里以java为例

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        @ResponseBody
        @RequestMapping(value="xxx")
        public String testJsonp(String callback) {
        Student result = new Student();
        //响应结果之前,判断是否为jsonp请求
        if (StringUtils.isNotBlank(callback)) {
        //把结果封装成一个js语句响应
        return callback + "(" + JsonUtils.objectToJson(result) + ");";
        }
        return JsonUtils.objectToJson(result);
        }
    • 后端进行 CORS 配置

    • 后端反向代理

      • 使用代理方式跨域更加直接,因为SOP的限制是浏览器实现的。如果请求不是从浏览器发起的,就不存在跨域问题了。

      • 使用本方法跨域步骤如下:

          1. 把访问其它域的请求替换为本域的请求
          1. 本域的请求是服务器端的动态脚本负责转发实际的请求
      • 首先在 conf\apiserver-reverse-proxy-conf\bingli\main.conf ,没有相关目录和文件就新建

        1
        2
        3
        4
        5
        6
        location ~* ^/uc/.*{
        proxy_set_header Host $host;
        proxy_set_header X-Real-Ip $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://192.168.10.111:8080;
        }
      • 然后在nginx主配置文件添加加粗内容,即把代理文件加载进来

        1
        2
        3
        4
        5
        location / {
        root html;
        index index.html index.htm;
        }
        include apiserver-reverse-proxy-conf/bingli/main.conf;
1
2
3
4
5
6
重启nginx,之后ajax发请求到
http://localhost/uc/aa
http://localhost/uc/bb?token=xxxx
都会被转发到
http://192.168.10.111:8080/uc/aa
http://192.168.10.111:8080/uc/bb?token=xxxx
  • 对于 iframe
    • 使用 location.hash 或 window.name 进行信息交流
    • 使用 postMessage

0x07 sop无法防御csrf

  • 我们通过原理就可以发现。CSRF是指 A 网站正常登陆后,cookie 正常保存,用户访问攻击者网站 B ,通过某种方式调用 A 网站接口进行操作,A 的接口在请求时会自动带上 cookie,来完成攻击
  • SOP 首先就是“禁止跨域请求”,这样描述也不是很合法。本质上 SOP 并不是禁止跨域请求,而是在请求后拦截了请求的回应。 发现,SOP 不阻止接口请求而是拦截请求结果。
  • 而CSRF 恰恰占了这两个便宜,所以 SOP 不能作为防范 CSRF 的方法。

0x08 SOP 与 ajax

  • 对于 ajax 请求,在获得数据之后你能肆意进行 js 操作。这时候虽然同源策略会阻止响应,但依然会发出请求。因为执行响应拦截的是浏览器而不是后端程序。事实上你的请求已经发到服务器并返回了结果,但是迫于安全策略,浏览器不允许你继续进行 js 操作,所以报出你熟悉的 blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.。
  • 所以再强调一次,同源策略不能作为防范 CSRF 的方法。
  • 跨域是浏览器限制