同源策略

208 阅读4分钟

关键词: 同源策略,CSP,跨文档通信API(Cross-document messaging), websocket origin, JSONP,CORS(coress-orgin resource sharing)

浏览器安全可以分为三大块——Web 页面安全、浏览器网络安全和浏览器系统安全。

同源策略: 如果两个URL的协议、域名和端口都相同,我们就称这两个 URL 同源。具体来讲,同源策略主要表现在 DOM、Web 数据和网络这三个层面。

  1. DOM 层面。同源策略限制了来自不同源的 JavaScript 脚本对当前 DOM 对象读和写的操作。
  2. 同源策略限制了不同源的站点读取当前站点的 Cookie、IndexDB、LocalStorage 等数据。
  3. 网络层面。同源策略限制了通过 XMLHttpRequest,Fetch 等方式将站点的数据发送给不同源的站点。

安全和便利性的权衡

  1. 页面中可以嵌入第三方资源 有时候页面需要引入一些不同源的资源,但是随便引入就会有安全问题。于是发明了CSP,核心思想是让服务器决定浏览器能够加载哪些资源,让服务器决定浏览器是否能够执行内联 JavaScript 代码。

  2. 跨域资源共享和跨文档消息机制 默认情况下,同源策略会阻止 XMLHTTPREQUEST 其向网站发出请求,这样会大大制约我们的生产力。为了解决这个问题,引入了跨域资源共享(CORS)Cross-Origin Resource Sharing

两个页面不是同源的,则无法相互操纵 DOM。不过在实际应用中,经常需要两个不同源的 DOM 之间进行通信,于是浏览器中又引入了跨文档消息机制,可以通过 window.postMessage 的 JavaScript 接口来和不同源的 DOM 进行通信。

cookie在同源策略的特殊性

Cookie 是服务器写入浏览器的一小段信息,只有同源的网页才能共享。但是,两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie。

举例来说,A网页是 w1.example.com/a.html ,B网页是w2.example.com/b.html ,那么只要设置相同的document.domain,两个网页就可以共享Cookie。

document.domain = 'example.com';

服务器也可以在设置Cookie的时候,指定Cookie的所属域名为一级域名,比如.example.com。

Set-Cookie: key=value; domain=.example.com; path=/

这样的话,二级域名和三级域名不用做任何设置,都可以读取这个Cookie。

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

片段识别符(fragment identifier,hash)
window.name
跨文档通信APICross-document messaging)

片段标识符(fragment identifier)指的是,URL的#号后面的部分,比如example.com/x.html#frag… 如果只是改变片段标识符,页面不会重新刷新。父窗口可以把信息,写入子窗口的片段标识符。子窗口通过监听hashchange事件得到通知。

window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。 举例来说,父窗口aaa.com向子窗口http://bbb.com 发消息,调用postMessage方法就可以了。

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

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

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

父窗口和子窗口都可以通过message事件,监听对方的消息。

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

message事件的事件对象event,提供以下三个属性。

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

为了打破Ajax的同源策略影响,有三个办法 JSONP,websocket,CORS:

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

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

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才没有实行同源政策。因为服务器可以根据这个字段,判断是否许可本次通信。如果该域名在白名单内,服务器就会做出如下回应。

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

参考: www.ruanyifeng.com/blog/2016/0… time.geekbang.org/column/arti…