同源策略 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. pythonchallenge总述

    Pythonchallenge是一个过关式的解谜站点,使用的是经典在线解谜站点Not Pr0n的模式:根据提示找出下一关的网页地址.和Not Pr0n不同的是,在每一关里你都需要编写程序来寻找答案.虽 ...

  2. 【noi 2.2_8758】2的幂次方表示(递归)

    题意:将正整数N用2的幂次方表示(彻底分解至2(0),2). 解法:将层次间和每层的操作理清楚,母问题分成子问题就简单了.但说得容易,操作没那么容易,我就打得挺纠结的......下面附上2个代码,都借 ...

  3. Codeforces Round #529 (Div. 3) E. Almost Regular Bracket Sequence (思维,模拟栈)

    题意:给你一串括号,每次仅可以修改一个位置,问有多少位置仅修改一次后所有括号合法. 题解:我们用栈来将这串括号进行匹配,每成功匹配一对就将它们消去,因为题目要求仅修改一处使得所有括号合法,所以栈中最后 ...

  4. Codeforces Round #654 (Div. 2) C. A Cookie for You (思维)

    题意:有\(a\)个蛋糕,\(b\)个巧克力,第一类人有\(n\)个,总是吃多的东西(若\(a>b\),吃蛋糕,否则吃巧克力),第二类人有\(m\)个,总是吃少的,可以随便调整这两类人吃的顺序, ...

  5. OkHttp Client Ignore certificate

    import okhttp3.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.*; i ...

  6. 02、scrapy安装

    1.安装scrapy 采用pip的安装方式,从豆瓣源获取 pip install -i https://pypi.douban.com/simple/ scrapy 安装过程中会报出错误: build ...

  7. HTTP笔记1--Web及网络基础

    web页面如何呈现? 客户端:通过发送请求获取服务器资源的 Web 浏览器 web是建立在 HTTP 协议上通信的   WWW(万维网/web)的构建技术 把 SGML(StandardGeneral ...

  8. Cobalt Strike特征隐藏

    前言 首先红蓝对抗的时候,如果未修改CS特征.容易被蓝队溯源. 前段时间360公布了cobalt strike stage uri的特征,并且紧接着nmap扫描插件也发布了.虽说这个特征很早就被发现了 ...

  9. Kubernetes部署Prometheus+Grafana(非存储持久化方式部署)

    1.在master节点处新建一个文件夹,用于保存下载prometheus+granfana的yaml文件 mkdir /root/prometheus cd /root/prometheus git ...

  10. redis运维与开发笔记