同源策略 Same Origin Policy

日常开发中最常与网络打交道,那关于浏览器的同源策略和跨域相关的知识是该整理一下了。

首先需要明确的是,同源策略是浏览器的安全策略,由于存在这个策略,我们才需要对各种跨域需求进行处理。

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

什么是同源

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

  • 协议相同
  • 域名相同
  • 端口相同
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 不同源

同源限制了什么

1、Cookie、localStorage 和 IndexDB 无法读取

2、DOM 无法获得

3、AJAX 请求不能发送

规避方法

1、读取 Cookie

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

可见 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 上一级的域名,否则会设置失败。本地测试的现象是没有报错,但是也没有设置成功。

2、读取 localStorage、IndexedDB 和 DOM

localStorage、IndexedDB 和 DOM 是实实在在受同源策略限制的,协议、域名和端口任意一项不相同就无法读取。

其实对于他们的读取可以简化成不同源的两个页面如何通信。

本身 localStorage 和 IndexedDB 的出现就是为了让我们能够更好地存储数据,而获取 DOM 大多情况也是为了获取 DOM 中的各种数据。泛化到业务场景中,也就是传递数据的事儿了。

目前可以通过以下几种方式来进行通信。

  • window.name

    使用 window.name 的原理是同源 iframe 可以读取 contentWindow。具体操作如下

    1、a 页面先载入一个不同源的 iframe 页面 b

    2、在 b 页面 中修改 window.name 为需要的传递的数据

    3、a 页面中修改这个 iframe 的 src 为同源的页面 c

    4、a 页面获取之前设置的 window.name

    按照这样的方式进行操作就能获取到子页面中的数据。如果子页面想要获取父页面中的数据,可以将 1、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 的长度有关。

    //父->子 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";

3、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)

前端常见跨域解决方案(全)

Same Origin Policy 浏览器同源策略详解的更多相关文章

  1. [js]浏览器同源策略(same-origin policy)

    浏览器的同源策略 浏览器同源政策及其规避方法 什么是同源策略 A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源".所谓"同源"指的是" ...

  2. 七牛云存储Python SDK使用教程 - 上传策略详解

    文 七牛云存储Python SDK使用教程 - 上传策略详解 七牛云存储 python-sdk 七牛云存储教程 jemygraw 2015年01月04日发布 推荐 1 推荐 收藏 2 收藏,2.7k  ...

  3. jsonp突破浏览器同源策略

    jsonp突破浏览器同源策略 <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  4. guava-retrying 源码解析(等待策略详解)

    一.等待策略相关类: 1.等待策略接口:WaitStrategy接口 该接口只有一个方法,就是返回尝试失败之后,下一次尝试之前的等待时间.long computeSleepTime(Attempt f ...

  5. django csrf_protect及浏览器同源策略

    1.django在检测post行为时会有诸多的限制. 为了防止跨域请求伪造安全 参考:http://www.qttc.net/201209211.html   https://www.cnblogs. ...

  6. 浏览器同源策略及Cookie的作用域

    from:https://blog.csdn.net/wang379275614/article/details/53333054 如题,本文主要介绍两方面内容:首先简单介绍浏览器的同源策略与其带来的 ...

  7. [转帖]Nginx服务器的六种负载均衡策略详解

    Nginx服务器的六种负载均衡策略详解 咔咔侃技术 2019-09-11 17:40:12 一.关于Nginx的负载均衡 在服务器集群中,Nginx起到一个代理服务器的角色(即反向代理),为了避免单独 ...

  8. Kubernetes 部署策略详解-转载学习

    Kubernetes 部署策略详解 参考:https://www.qikqiak.com/post/k8s-deployment-strategies/ 在Kubernetes中有几种不同的方式发布应 ...

  9. 踩坑录- Spring Boot - CORS 跨域 - 浏览器同源策略

    1.解决办法,创建一个过滤器,处理所有response响应头 import java.io.IOException; import javax.servlet.Filter; import javax ...

随机推荐

  1. OpenCV4.4.0 安装测试 Installation & Examination (Ubuntu18.04, Ubuntu 20.04)

    OpenCV4.4.0 安装测试 Installation & Examination (Ubuntu18.04, Ubuntu 20.04) 单纯简单的 OpenCV 安装配置方法,在这个地 ...

  2. A - The Suspects (sars传染)

    题意:有m组,0为起点,有0的那一组全是嫌疑人,之后会不断传递到其它组去,问一共有多少人是嫌疑人. Severe acute respiratory syndrome (SARS), an atypi ...

  3. P2805 [NOI2009]植物大战僵尸 (拓扑排序 + 最小割)

    题意:N*M的矩阵 每个点上都有一颗植物 僵尸只能从每一行的最右边向左进攻 每个植物有攻击范围 可以保护在攻击范围内的植物 同时每一颗植物也保护他左边的植物 摧毁每个植物能获得价值 如果这个植物被保护 ...

  4. codeforces 630K Indivisibility (容斥原理)

    IT City company developing computer games decided to upgrade its way to reward its employees. Now it ...

  5. Codeforces Round #305 (Div. 1) B. Mike and Feet

    Mike is the president of country What-The-Fatherland. There are n bears living in this country besid ...

  6. Codeforces Round #649 (Div. 2) B. Most socially-distanced subsequence (数学,差分)

    题意:有一长度为\(n\)的数组,求一子序列,要求子序列中两两差的绝对值最大,并且子序列尽可能短. 题解:将数组看成坐标轴上的点,其实就是求每个单调区间的端点,用差分数组来判断单调性. 代码: #in ...

  7. Educational Codeforces Round 56 (Rated for Div. 2) D. Beautiful Graph (二分图染色)

    题意:有\(n\)个点,\(m\)条边的无向图,可以给每个点赋点权\({1,2,3}\),使得每个点连的奇偶不同,问有多少种方案,答案对\(998244353\)取模. 题解:要使得每个点所连的奇偶不 ...

  8. 开源RPA软件试用

      优点 缺点 其它 Robot Framework 可视化界面 运行环境搭建复杂,依赖较多 操作复杂 倾向于自动化测试 TagUI 浏览器支持好 官方文档详细 命令行操作 非浏览器程序支持一般   ...

  9. MySQL中为避免索引失效所需注意的问题

    一.索引介绍 二.索引的优势与劣势 1.优势 类似于书籍的目录索引,提高数据检索的效率,降低数据库的IO成本. 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗. 2.劣势 实际上索引也 ...

  10. Kubernets二进制安装(4)之Docker安装

    注意:需要安装Docker的机器为mfyxw30.mfyxw40.mfyxw50 集群规划 主机名 角色 IP地址 mfyxw30.mfyxw.com Docker 192.168.80.30 mfy ...