前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浏览器同源策略及规避方式

浏览器同源策略及规避方式

作者头像
OECOM
发布2020-07-02 09:27:47
1.4K0
发布2020-07-02 09:27:47
举报
文章被收录于专栏:OECOM

同源,何为同源,同源的意思就是协议、端口、域名三者均需要相同才能构成同源。例如 这个域名来说,https://为协议,www.oecom.cn为域名,默认的端口为80端口。

代码语言:javascript
复制
https://www.oecom.cn:8080 //端口不同,不同源
http://www.oecom.cn//协议不同,不同源
https://oecom.cn//域名不同,不同源

同源策略是浏览器的一个安全基石,保证用户信息的安全,防止恶意的网站窃取数据。

随着互联网的发展,"同源政策"越来越严格,基本上会有一下几种情况受到同源策略的制约

代码语言:javascript
复制
1 .Cookie、LocalStorage 和 IndexDB 无法读取。
2. DOM 无法获得。
3. AJAX 请求不能发送。

虽然这些限制是必要的,很好的防止数据被其他网站恶意窃取修改。但是合理的用途也会受到影响。

共享Cookie

cookie是服务器端和浏览器端都可以读写的信息存储方式,只有同源的网页才可以共享。但是,两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie。

例如:https://a.oecom.cn下有一个a.html,在https://b.oecom.cn下有一个b.html,这两个页面需要共享cookie,由于两个页面都是oecom.cn的二级域名,均可以采用设置document.domain的方式来进行跨域。

js中加入document.domain='oecom.cn',这样就已经设置好了,这种方法适用于 Cookie 和 iframe 窗口。

当然也可以通过服务器端进行设置,这样在二级、三级域名下都可以共享cookie

代码语言:javascript
复制
Set-Cookie: key=value; domain=.example.com; path=/

利用document.domain 实现跨域有前提条件:这两个域名必须属于同一个基础域名!而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域

获取ifreamDOM节点

如果两个网页不同源,就无法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。

比如,父窗口运行下面的命令,如果iframe窗口不是同源,就会报错

代码语言:javascript
复制
document.getElementById("myIFrame").contentWindow.document
// Uncaught DOMException: Blocked a frame from accessing a cross-origin frame.

上面命令中,父窗口想获取子窗口的DOM,因为跨源导致报错。反之亦然,子窗口获取主窗口的DOM也会报错。

代码语言:javascript
复制
window.parent.document.body
// 报错

如果两个窗口一级域名相同,只是二级域名不同,那么设置上面所提到的document.domain属性,就可以规避同源政策,拿到DOM。

对于完全不同源的网站,目前有三种方法,可以解决跨域窗口的通信问题:

1. 片段识别符(fragment identifier)

2. window.name

3.跨文档通信API(Cross-document messaging)

片段识别符(fragment identifier)

片段识别符指的是#后面的部分,例如:https://www.oecom.cn/index.html#fragment,#fragment就是片段识别符,通过修改片段识别符不会导致刷新页面。

代码语言:javascript
复制
var src = originURL + '#' + data;//originURL为原ifream的地址,data为要传输的数据
document.getElementById('myIFrame').src = src;

子窗口可以通过监听hashchange事件来得到通知。

代码语言:javascript
复制
window.onhashchange = checkMessage;

function checkMessage() {
  var message = window.location.hash;
  // dosomething...
}

同样的,子窗口也可以改变父窗口的片段标识符。

代码语言:javascript
复制
parent.location.href= target + "#" + hash;//target为父窗口的原URL
window.name

浏览器窗口有一个属性叫做window.name,这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。父窗口先打开一个子窗口,载入一个不同源的网页,该网页将信息写入window.name属性:window.name = data。

接着,子窗口跳回一个与主窗口同域的网址。

代码语言:javascript
复制
location = 'http://parent.url.com/xxx.html';

然后,主窗口就可以读取子窗口的window.name了。

代码语言:javascript
复制
var data = document.getElementById('myFrame').contentWindow.name;

这种方法的优点是,window.name容量很大,可以放置非常长的字符串;缺点是必须监听子窗口window.name属性的变化,影响网页性能。

window.postMessage

在HTML5中,为了解决跨域通信问题,提供了一个全新的API:即跨文档通信API。这个API为window对象增加了一个window.postMessage,它允许跨窗口通信,不局限于是否同源。

例如a窗口向b窗口发送消息

代码语言:javascript
复制
<div style="width:200px; float:left; margin-right:200px;border:solid 1px #333;">
        <div id="color">Frame Color</div>
    </div>
    <div>
        <iframe id="child" src="http://a.com/a.html"></iframe>
    </div>

我们可以在http://test.com/index.html通过postMessage()方法向跨域的iframe页面http://a.com/a.html传递消息

代码语言:javascript
复制
window.onload=function(){
            window.frames[0].postMessage('getcolor','http://a.com');
        }

postMessage(data,origin)方法接受两个参数:

1. data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。

2. origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

消息发送完成以后必须接收才能有意义,方法就是监听window的message事件。例如在http://a.com/a.html页面可以这样写

代码语言:javascript
复制
window.addEventListener('message',function(e){
                if(e.source!=window.parent) return;//只接收父窗口的数据
                var color=container.style.backgroundColor;
                window.parent.postMessage(color,'*');
            },false);

这样我们就可以接收任何窗口传递来的消息了。

通过postMessage来传输数据以后,极大的方便了数据的传输,子窗口接收到信息以后可以将信息存储到cookie或者是localStorage中,父窗口也可以将自己的cookie或localStorage中的数据传输到子窗口,这样变相达到了两者存储方式的跨域。

Ajax

同源政策规定,AJAX请求只能发给同源的网址,否则就报错。除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),有三种方法规避这个限制:JSONP、WebSocket 、CORS。

JSONP

JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。

它的基本思想是,网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

首先,网页动态插入<script>元素,由它向跨源网址发出请求

代码语言:javascript
复制
function addScriptTag(src) {
  var script = document.createElement('script');
  script.setAttribute("type","text/javascript");
  script.src = src;
  document.body.appendChild(script);
}

window.onload = function () {
  addScriptTag('http://example.com/ip?callback=foo');
}

function foo(data) {
  console.log('Your public IP address is: ' + data.ip);
};

上面代码通过动态添加<script>元素,向服务器example.com发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于JSONP是必需的。

服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。

代码语言:javascript
复制
foo({
  "ip": "8.8.8.8"
});

由于<script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了<foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。

WebSocket

WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。

下面是一个例子,浏览器发出的WebSocket请求的头信息

代码语言:javascript
复制
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

上面代码中,有一个字段是Origin,表示该请求的请求源(origin),即发自哪个域名。

正是因为有了Origin这个字段,所以WebSocket才没有实行同源政策。因为服务器可以根据这个字段,判断是否许可本次通信。如果该域名在白名单内,服务器就会做出如下回应。

代码语言:javascript
复制
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
CORS

CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。相比JSONP只能发GET请求,CORS允许任何类型的请求。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-12-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 共享Cookie
  • 获取ifreamDOM节点
    • 片段识别符(fragment identifier)
      • window.name
        • window.postMessage
        • Ajax
          • JSONP
            • WebSocket
              • CORS
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档