Same Origin Policy 浏览器同源策略详解

同源策略 Same Origin Policy

平常开发中最常与网络打交道,那关于浏览器的同源策略和跨域相关的知识是该整理一下了。
首先须要明确的是,同源策略是浏览器的安全策略,因为存在这个策略,咱们才须要对各类跨域需求进行处理。html

同源策略的主要目的是为了保护用户的信息安全。前端

什么是同源

同源的含义其实比较好理解,实际上就是三点ajax

  • 协议相同
  • 域名相同
  • 端口相同
url 说明 是否同源
http://www.test.com https://www.test.com 同一域名,不一样协议 不一样源
http://www.test.com https://www.test1.com 不一样域名 不一样源
http://www.test.com:8080 https://www.test.com:8081 同一域名,不一样端口 不一样源
https://www.test.com http://192.168.1.1 域名和域名对应的 ip 不一样源

同源限制了什么

一、Cookie、localStorage 和 IndexDB 没法读取
二、DOM 没法得到
三、AJAX 请求不能发送算法

规避方法

cookie 的读取与其余属性有些不一样。由于 cookie 的可见性是由 domain 属性和 path 属性决定的,因此他其实不受协议和端口的限制。只要是同一个 domain 和可见 path 下的 cookie,都能读取到。segmentfault

可见 path

  • / 只能读取 / 下的 cookie
  • /test 能够读取 / 和 /test 下的 cookie

因为 cookie 的读取自己具备局限性,因此跨域后的规避能够这样操做跨域

  • 前端读取不一样 domain 的 cookie,能够把经过设置 document.domain 来读取相应 cookie。固然,你设置的 domain 必须是你当前 domain 的上一级域名,不然会报以下的错误。
    Uncaught DOMException: Failed to set the 'domain' property on 'Document': 'test' is not a suffix of 'localhost'.
  • 前端和服务器端设置 cookie 时指定可读的 domian 和 path,这个 domain 也遵循上面的规则,只能设置当前 domain 或 domain 上一级的域名,不然会设置失败。本地测试的现象是没有报错,可是也没有设置成功。

二、读取 localStorage、IndexedDB 和 DOM

localStorage、IndexedDB 和 DOM 是实实在在受同源策略限制的,协议、域名和端口任意一项不相同就没法读取。
其实对于他们的读取能够简化成不一样源的两个页面如何通讯。
自己 localStorage 和 IndexedDB 的出现就是为了让咱们可以更好地存储数据,而获取 DOM 大多状况也是为了获取 DOM 中的各类数据。泛化到业务场景中,也就是传递数据的事儿了。浏览器

目前能够经过如下几种方式来进行通讯。安全

  • window.name
    使用 window.name 的原理是同源 iframe 能够读取 contentWindow。具体操做以下
    一、a 页面先载入一个不一样源的 iframe 页面 b
    二、在 b 页面 中修改 window.name 为须要的传递的数据
    三、a 页面中修改这个 iframe 的 src 为同源的页面 c
    四、a 页面获取以前设置的 window.name
    按照这样的方式进行操做就能获取到子页面中的数据。若是子页面想要获取父页面中的数据,能够将 一、3 步骤换一下,2 步骤改为父页面直接修改 iframe.contentWindow.name 为须要传递的数据便可。
    由于是经过 name 属性来传递参数,因此可传递的数据量很大,基本就是字符串长度的最大值。服务器

    //记录 iframe onload 事件的加载次数
    var state = 0;
    var iframe = document.createElement("iframe");
    // 加载跨域页面
    iframe.src = "http://sub.test.com/test/test.html";
    // onload 事件会触发2次,第1次加载跨域页,在跨域页中将所须要的数据赋给 window.name
    iframe.onload = function () {
      if (state === 1) {
        // 第2次 onload 成功后,读取同域 window.name 中数据
        var data = iframe.contentWindow.name;
      } else if (state === 0) {
        // 第1次onload(跨域页)成功后,切换到同域代理页面
        iframe.contentWindow.location = "./testB.html";
        state = 1;
      }
    };
    document.body.appendChild(iframe);
  • fragment identifier 片断标识符
    fragment identifier 其实就是 url # 后面的部分。常写 SPA 应用的小伙伴应该会对他很熟悉,由于 hash 模式的 router 就是基于他实现的。修改嵌入的 iframe 的 src 为 url#须要传递的数据,iframe 页面就能经过监听 hashchange 事件得到传递的数据。若是是子页面向父页面传递数据要多加一步,得先让父页面把本身当前的 url 传递给子页面,而后子页面去修改父页面的 href。
    使用这个方法传递数据的限制暂时还未测试,猜想应该会和浏览器限制 url 的长度有关。cookie

    //父->子 a页面
    var iframe = document.createElement("iframe");
    // 加载跨域页面
    iframe.src = url;
    setTimeout(function () {
      iframe.src = url + "#a=111";
    }, 1000);
    document.body.appendChild(iframe);
    
    //父->子 b页面
    window.onhashchange = function () {
      alert(window.location.hash)
    }
    //子->父 a页面
    var iframe = document.createElement("iframe");
    // 加载跨域页面
    iframe.src = url;
    setTimeout(function () {
      var selfurl = document.location.href;
      iframe.src = url + "#" + selfurl;
    }, 1000);
    document.body.appendChild(iframe);
    window.onhashchange = function () {
      alert("from son" + window.location.hash);
    };
    
    //子->父 b页面
    window.onhashchange = function () {
      let target = window.location.hash.slice(1);
      window.parent.parent.location.href = target + "#111aaaa"
    }
  • postMessage
    严格意义上来讲上面两种方法都是对跨域页面获取数据的破解,postMessage 才是正统的非同源页面之间传递数据的方法。

    window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。

    postMessage 是新增的 API,使用前记得查看一下兼容性。
    使用上须要注意的就是调用 postMessage 和监听 message 事件的的主体是同一个。也就是说子页面向父页面发送消息时,须要获取 window.parent 去调用 postMessage。父页面向子页面发送消息时,能够直接获取 iframe,而后调用 postMessage。

    postMessage((message, targetOrigin, [transfer]);
    message
    将要发送到其余 window 的数据。它将会被结构化克隆算法序列化。这意味着你能够不受什么限制的将数据对象安全的传送给目标窗口而无需本身序列化。
    targetOrigin
    经过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值能够是字符串""(表示无限制)或者一个 URI。
    在发送消息的时候,若是目标窗口的协议、主机地址或端口这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会被发送;只有三者彻底匹配,消息才会被发送。
    这个机制用来控制消息能够发送到哪些窗口;例如,当用 postMessage 传送密码时,这个参数就显得尤其重要,必须保证它的值与这条包含密码的信息的预期接受者的 origin 属性彻底一致,来防止密码被恶意的第三方截获。
    若是你明确的知道消息应该发送到哪一个窗口,那么请始终提供一个有确切值的 targetOrigin,而不是
    。不提供确切的目标将致使数据泄露到任何对数据感兴趣的恶意站点。
    transfer 可选
    是一串和message 同时传递的 Transferable 对象. 这些对象的全部权将被转移给消息的接收方,而发送一方将再也不保有全部权

    下面这个示例是子页面向父页面发送消息。

    // a 页面
    window.addEventListener("message", function (event) {
      console.log(event);
    }, false);
    
    // b 页面
    var b = "bbb";
    window.parent.postMessage({
      b
    }, "*")
  • 另外特别须要注意的是关于 DOM 的获取,若是只是两个不一样子域的页面,将 document.domain 设置为同一主域就能够读取相应数据。

    //a 页面 main.test.com
    var a = "aaa";
    // domain 设置为主域
    document.domain = "test.com";
    var iframe = document.createElement("iframe");
    // 加载跨域页面
    iframe.src = "sub.test.com";
    document.body.appendChild(iframe);
    iframe.onload = function(){
      // 能够获取到变量
      var data = iframe.contentWindow.b
      // 能够获取到 DOM
      var dom = iframe.contentWindow.document.body;
    }
    
    //b 页面 sub.test.com
    var b = "bbb";
    // domain 设置为主域
    document.domain = "test.com";

三、AJAX 请求

AJAX 请求跨域平常使用比较多,经常使用的方法有如下几种

  • JSONP
    这个方法是向服务器请求的时候,在 url 后面写上 callback 方法的名字,请求返回其实是返回了一个 调用 callback 方法的 js 文件。须要返回的参数也就在调用的时候传进去了。
    因此局限也很明确,只支持 get 方法。

  • 使用代理服务器
    全部的请求先发送给这个代理服务器,由这个代理服务器去请求实际的接口,再把须要的数据返回。
    (这个方式就能够真切地体会到,同源策略只是浏览器的一种安全策略)

  • 使用 CORS Cross-Origin Resource Sharing 跨域资源共享
    目前基本上全部的浏览器都支持 CORS,因此只须要服务器端进行处理。对于前端来讲,请求和同源的 AJAX 请求是一致的。
    前端发送请求的时候浏览器会自动带上 origin 字段,服务器端去判断这个 origin 是不是可接受的地址,在相应头中设置 Access-Control-Allow-Origin 字段的值。
    这样前端就能正常获取到数据啦。
    这里只对 CORS 作了一个简单介绍,详细的下次细说吧。

总结:

  • 读取跨域的 cookie 须要设置 path 和 domain
  • 不一样源页面间通信能够经过设置 window.name、location 的 hash 值和 postMessage 来实现。
    不难发现的是,设置 hash 和 postMessage 都是经过设置一个监听函数来实现的,因此咱们是异步获取数据的。
    而设置 window.name 虽然是在 iframe.onload 事件中获取的,可是本质上是在等待 iframe 的加载,确保数据已经设置成功。
    这里还有值得思考的地方。

参考资料

浏览器同源政策及其规避方法
浏览器的同源策略
window.postMessage
跨源资源共享(CORS)
前端常见跨域解决方案(全)

相关文章
相关标签/搜索