浏览器同源策略以及跨域解决方法

浏览器的同源策略:
同源策略是一种约定,它是浏览器最核心也是最基本的安全功能,如果缺少同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。只允许访问来自同一站点的资源,而不是那些来自其它站点可能带有恶意代码的资源。
所谓同源是指:域名、协议、端口相同。
同源策略还可以分为两种:
1.DOM同源策略:禁止对不同源页面DOM进行操作。这里主要场景是iframe跨域的情况,不同域名的iframe是限制相互访问的。
2.XMLHttpRequest同源策略:禁止使用XHR对象向不同源的服务器地址发起HTTP请求
为什么要有跨域限制:
因为存在浏览器同源策略,所以才会有跨域,跨域限制主要的目的是为了用户的上网安全。
如果浏览器没有同源策略,会存在什么样的安全问题。从DOM同源策略和XMLHttpRequest同源策略来举例说明:
如果没有DOM同源策略,也就是说不同域的iframe之间可以相互访问,那么黑客可以这样进行攻击:
1.做一个假网站,里面用iframe嵌套一个银行网站http://mybank.com
2.把iframe宽高啥上网调整到页面全部,这样用户进来除了域名,别的部分和银行网站没有任何差别。
3.这时如果用户输入账号密码,我们的主网站可以跨域访问到http://mybanck.com的dom节点,就可以拿到用户的账户密码
如果XMLHttpRequest同源策略,那么黑客可以进行CSRF(跨站请求伪造)攻击:
1.用户登录了自己的银行页面http://mybank.com,http://mybank.com向用户的cookie中同时发送过去
2.用户浏览了恶意页面http://evil.com,执行了页面中的恶意AJAX请求代码:
3.http://evil.com向http?/mybank.com发起AJAX HTTP请求,请求会默认把http://mybank.com对应cookie也同时发送过去。
4.银行页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了。
5.而且由于Ajax在后台执行,用户无法感知这一过程。
跨域的解决方法:
CORS(跨域资源共享)需要浏览器与服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CROS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CROS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨域,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CROS接口,就可以跨源通信。
浏览器将CORS请求分成两类:简单请求和非简单请求。
只要同时满足以下两大条件,就属于简单请求
1.请求方法是以下三种方式之一:HEAD、GET、POST
2.HTTP的头信息不超出以下几种字段:
Accept、Accept-language、Content-Language、last-Event-ID、Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/palin
凡是不同时满足上面条件,就属于非简单请求。
简单请求:
1.在请求中需要附加一个额外的Origin头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是都给予响应。例如:

Origin:http://www.ceshi.com

2如果服务器认为这个请求可以接受,就在Access-Control-Allow-Origin头部中回发相同的源信息(如果是公用资源,可以回发*)。例如

Access-Control-Allow-Origin:http://www.ceshi.com

没有这个头部或者有这个头部但源信息不匹配,浏览器就会驳回请求。正常情况下,浏览器会处理请求。注意,请求个响应都不包含cookie信息。
4.如果需要包含cookie信息,ajax请求需要设置xhr的属性withCredentials为true,服务器需要设置响应头部

Access-Control-Allow-Credentials:true

非简单请求:
浏览器在发送真正的请求之前,会先发送一个Preflight请求给服务器,这种请求使用OPTIONS方法,发送下列头部:
Origin:与简单的请求相同。
Access-Control-Request-Method:请求自身使用的方法。
Access-Control-Request-Headers:(可选)自定义的头部信息,多个头部以逗号分隔。例如:

Origin:http://www.ceshi.com
Access-Control-Request-Method:post
Access-Control-Request-Headers:NCZ

发送这个请求后,服务器可以决定是否允许这种类型的请求。服务器通过在响应中发送如下头部与浏览器进行沟通:
Access-Control-Allow-Origin:与简单的请求相同
Access-Control-Request-Method:允许的方法,多个方法以逗号分隔
Access-Control-Request-Headers:允许的头部,多个方法以逗号分隔
Access-Control-Max-Age:应该将这个Preflight请求缓存多长时间(以秒表示)

Access-Control-Allow-Origin:http://www.ceshi.com
Access-Control-Request-Method:post
Access-Control-Request-Headers:NCZ
Access-Control-Max-Age:1728000

一旦服务器通过Preflight请求允许该请求之后,以后每次浏览器正常的CORS请求,就都跟简单请求一样了。
优点:
CORS通信与同源的AJAX通信没有差别,代码完全一样,容易维护。
支持所有类型的HTTP请求
缺点:
存在兼容性问题,特别是IE10以下的浏览器
第一次方发送非简单请求时会多一次请求
JSONP跨域
由于script标签不受浏览器同源策略的影响,允许跨域引用资源。因此可以通过动态创建script标签,然后利用src属性进行跨域,这也就是JSONP跨域基本原理

function jsonpCallback(result){
	console.log(result);
}
var script=document.createElement("script");
script.type="text/javascript";
script.src="http://127.0.0.0/index.php?callback=jsonpCallback";//传递的参数作为函数名返回
document.getElementsByTagName("head")[0].appendChild("script");

优点:使用简单,没有兼容性问题,目前最流行的一种跨域方法
缺点:只支持GET请求,由于是从其它域中加载代码执行,因此如果其他域不安全,很可能在响应中夹带一些恶意代码
图片Ping跨域
由于img标签不受浏览器同源策略的影响,允许跨域引用资源。因此可以通过img标签的src属性进行跨域,这也就是图像Ping跨域的基本原理。基本通过下面的例子来说明图像Ping实现跨域的流程

var img=new Image();
// 通过 onload 及 onerror 事件可以知道响应是什么时候接收到的,但是不能获取响应文本
img.onload=img.onerror=function(){
	console.log("ok!");
}
//请求数据通过查询字符串形式发送
img.src='http://www.ceshi.com/test?name=yuanfang';

优点:
用于实现跟踪用户点击页面或动态广告曝光次数有较大的优势
缺点:
只支持GET请求
只能浏览器与服务器的单向通信,因为浏览器不能浏览访问服务器的响应文本
服务器代理:
浏览器有跨域限制,但是服务器不存在跨域问题,所以可以由服务器请求所要域的资源再返回给客户端
document.domain跨域
对于主域名相同,而子域名不同的情况,可以使用 document.domain 来跨域。这种方式非常适用于 iframe 跨域的情况。
比如,有一个页面,它的地址是 http://www.ceshi.com/a.html,在这个页面里面有一个 iframe,它的 src 是 http://ceshi.com/b.html。很显然,这个页面与它里面的 iframe 框架是不同域的,所以我们是无法通过在页面中书写 js 代码来获取 iframe 中的东西的。这个时候,document.domain 就可以派上用场了,我们只要把http://www.ceshi.com/a.html 和 http://ceshi.com/b.html 这两个页面的 document.domain 都设成相同的域名就可以了。但要注意的是,document.domain 的设置是有限制的,我们只能把 document.domain 设置成自身或更高一级的父域,且主域必须相同。例如:a.b.ceshi.com 中某个文档的 document.domain 可以设成 a.b.ceshi.comb.ceshi.com 、ceshi.com中的任意一个,但是不可以设成 c.a.b.ceshi.com,因为这是当前域的子域,也不可以设成 baidu.com,因为主域已经不相同了。
例如,在页面 http://www.ceshi.com/a.html 中设置document.domain:

<iframe src="http://ceshi.com/b.html" id="myIframe" onload="test()">
<script>
    document.domain = 'ceshi.com'; // 设置成主域
    function test() {
        console.log(document.getElementById('myIframe').contentWindow);
    }
</script>

在页面 http://ceshi.com/b.html 中也设置 document.domain,而且这也是必须的,虽然这个文档的 domain 就是ceshi.com,但是还是必须显式地设置 document.domain 的值:

<script>
    document.domain = 'ceshi.com'; // document.domain 设置成与主页面相同
</script>

这样http://www.ceshi.com/a.html就可以通过js访问到http://ceshi.com/b.html中的各种属性和对象了
window.name跨域
window对象有一个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有页面(不管是相同域的页面还是不同域的页面)都是共享一个 window.name 的,每个页面对 window.name 都有读写的权限,window.name 是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
通过下面的例子介绍如何通过window.name来跨域获取数据的
页面http://www.ceshi.com/a.html的代码

<iframe src="http://ceshi.com/b.html" id="myIframe" onload="test()" style="display: none;">
<script>
    function test() {
        var iframe = document.getElementById('myIframe');
        iframe.onload = function() {
            var data = iframe.contentWindow.name; //获取 iframe 里的 window.name
            console.log(data); // hello world!
        };
        
        //重置一个与 http://www.ceshi.com/a.html 页面同源的页面
        iframe.src = 'http://www.ceshi.com/c.html';
    }
</script>

页面http://ceshi.com/b.html的代码

<script type="text/javascript">
    // 给当前的 window.name 设置一个 http://www.ceshi.com/a.html 页面想要得到的数据值 
    window.name = "hello world!";
</script>

location.hash跨域
location.hash 方式跨域,是子框架具有修改父框架 src 的 hash 值,通过这个属性进行传递数据,且更改 hash 值,页面不会刷新。但是传递的数据的字节数是有限的。
页面http://www.ceshi.com/a.html

<iframe src="http://ceshi.com/b.html" onload="test()" style="display:none;">
<script>
	function test(){
		var data=window.location.hash;
		console.log(data);	
	}
</script>

页面http://ceshi.com/b.html的代码

<script type="text/javascript">
	//设置父页面的hash值
	parent.location.hash="world";
<script>

postMessage跨域
window.postMessage(message,targetOrigin) 方法是 HTML5 新引进的特性,可以使用它来向其它的 window 对象发送消息,无论这个 window 对象是属于同源或不同源。这个应该就是以后解决 dom 跨域通用方法了。
调用 postMessage 方法的 window 对象是指要接收消息的那一个 window 对象,该方法的第一个参数 message 为要发送的消息,类型只能为字符串;第二个参数 targetOrigin 用来限定接收消息的那个 window 对象所在的域,如果不想限定域,可以使用通配符 *。
需要接收消息的 window 对象,可是通过监听自身的 message 事件来获取传过来的消息,消息内容储存在该事件对象的 data 属性中。
页面http;//www…ceshi.com/a,html的代码

<iframe src="http://ceshi.com/b.html" id="myIframe" onload="test()" style="display: none;">
<script>
    // 1. iframe载入 "http://ceshi.com/b.html 页面后会执行该函数
    function test() {
        // 2. 获取 http://ceshi.com/b.html 页面的 window 对象,
        // 然后通过 postMessage 向 http://ceshi.com/b.html 页面发送消息
        var iframe = document.getElementById('myIframe');
        var win = iframe.contentWindow;
        win.postMessage('我是来自 http://www.ceshi.com/a.html 页面的消息', '*');
    }
</script>

页面http;//ceshi.com/b.html的代码

<script type="text/javascript">
    // 注册 message 事件用来接收消息
    window.onmessage = function(e) {
        e = e || event; // 获取事件对象
        console.log(e.data); // 通过 data 属性得到发送来的消息
    }
</script>

转载链接:https://www.cnblogs.com/laixiangran/p/9064769.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值