浏览器-同源政策(same-origin policy)

浏览器安全的基石是“同源政策”(same-origin policy)。

1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。

何为同源?

  • 协议相同
  • 域名相同
  • 端口相同

限制范围

  • Cookie、LocalStorage 和 IndexedDB 无法读取。
  • DOM 无法获得。
  • AJAX 请求不能发送。

  PS:Form表单提交不受同源政策限制;

一级域名相同,二级域名不同网页Cookie 和 iframe规避同源政策

一级域名相同,只是二级域名不相同的2个网页(A页和B页)间通信(共享cookie和ifrmame DOM):

//A网页通过脚本设置一个 Cookie
document.cookie = "test1=hello";

//B网页不能读取
var allCookie = document.cookie;

//父窗口不能读取子iframe的DOM对象,报错
document.getElementById("myIFrame").contentWindow.document

//同样子窗口不能获取父窗口的元素,报错
window.parent.document.body

如果两个窗口一级域名相同,只是二级域名不同,那么设置document.domain属性,就可以规避同源政策,共享cookie和获取DOM,解决上面的问题:

//设置为一级域名,规避同源政策
document.domain = 'example.com';

完全不同源网站解决跨域窗口间通信问题

有2种方法:

  • 片段识别符(fragment identifier)
  • 跨文档通信API(Cross-document messaging)

片段识别符(fragment identifier),指的是URL的#号后面的部分,将此作为两个窗口通信内容的中介存储,可互为读取,实现通信。

父窗口把信息写入子窗口的片段识别符:

var src = originURL + '#' + data;
document.getElementById('myIFrame').src = src;

子窗口通过监听hashchange事件,得到通知:

window.onhashchange = checkMessage;

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

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

parent.location.href= target + “#” + hash;

上面两种方法都属于破解,HTML5为了解决这个问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)。

这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源:

 父窗口向子窗口发送消息:

var popup = window.open('http://bbb.com', 'title');
popup.postMessage('Hello World!', 'http://bbb.com');

子窗口向父窗口发送消息:

window.opener.postMessage('Nice to see you', 'http://aaa.com');

postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即“协议 + 域名 + 端口”。也可以设为*,表示不限制域名,向所有窗口发送。

父窗口或子窗口监听对方发来的消息:

window.addEventListener('message', function(e) {
  console.log(e.data);
},false);

message事件的事件对象提供3个属性:

  • event.source:发送消息的窗口
  • event.origin: 消息发向的网址
  • event.data: 消息内容

子窗口通过event.source属性引用父窗口,然后回复消息:

window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
  event.source.postMessage('Nice to see you!', '*');
}

event.origin属性可以过滤不是发给本窗口的消息:

window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
  if (event.origin !== 'http://aaa.com') return;
  if (event.data === 'Hello World') {
      event.source.postMessage('Hello', event.origin);
  } else {
    console.log(event.data);
  }
}

一个跨文档通信API案例:

//父窗口发送消息的代码
var win = document.getElementsByTagName('iframe')[0].contentWindow;
var obj = { name: 'Jack' };
win.postMessage(JSON.stringify({key: 'storage', data: obj}), 'http://bbb.com');

//子窗口接收消息,写入localStorage
window.onmessage = function(e) {
  if (e.origin !== 'http://bbb.com') {
    return;
  }
  var payload = JSON.parse(e.data);
  localStorage.setItem(payload.key, JSON.stringify(payload.data));
};

AJAX与同源政策

同源政策规定,AJAX请求只能发给同源的网址,否则就报错。

AJAX有3种方法规避同源政策:

  • JSONP
  • WebSocket
  • CORS

第1种:JSONP

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

它的基本思想是,网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据以一个指定名字的回调函数的参数形式传回客户端,客户端在约定名字的回调函数中处理接收到的参数 。

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

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);
};

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

foo({
  "ip": "8.8.8.8"
});

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

参考

同源政策,阮一峰 http://javascript.ruanyifeng.com/bom/same-origin.html 

原文地址:https://www.cnblogs.com/ybtools/p/6733642.html