同源策略

同源策略

同源策略(Same Origin Policy, SOP)是Web应用程序的一种安全模型,它控制了网页中DOM之间的访问。
同源三要素:主机协议端口

同源策略作用

浏览器的同源策略会导致跨域,这里同源策略又分为以下两种:

  • DOM同源策略:禁止对不同源页面DOM进行操作。这里主要场景是iframe跨域的情况,不同域名的iframe是限制互相访问的。
  • XmlHttpRequest同源策略:禁止使用XHR对象向不同源的服务器地址发起HTTP请求。

同源策略没有禁止脚本的执行,而是禁止读取HTTP回复

我们会发现,SOP其实在防止CSRF上作用非常有限,CSRF的请求往往在发送出去的那一瞬间就已经达到了攻击的目的,比如发送了一段敏感数据,或请求了一个具体的功能,是否能读取回复并不那么重要(唯一的作用是可以防止CSRF请求读取异源的授权Token)。 另外,一般静态资源通常不受同源策略限制,如js/css/jpg/png等。

要注意,这里的CSRF攻击指的是通过链接或者脚本去访问恶意网站的情况,而不是通过ajax访问的情况,ajax的同源策略正是为了防止CSRF攻击。

跨源访问

同源策略控制了不同源之间的交互,例如在使用XMLHttpRequest<img> 标签时则会受到同源策略的约束。交互通常分为三类:

  • 跨域写(Cross-origin writes):通常被允许,例如链接,重定向和表单提交,一些不常见的HTTP请求方法例如PUT,DELETE等需要先发送预请求(preflight),例如发送OPTIONS来查询可用的方法。
  • 跨域嵌入(Cross-origin embedding):通常被允许
  • 跨域读:通常被禁止,然而,我们可以用其他方法达到读取的效果。

以下是一些跨域请求的例子:

  • <script src="..."></script>标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。
  • <link rel="stylesheet" href="...">标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的Content-Type消息头。不同浏览器有不同的限制: IE, Firefox, Chrome, Safari (跳至CVE-2010-0051)部分 和 Opera。
  • <img>嵌入图片。支持的图片格式包括PNG,JPEG,GIF,BMP,SVG,…
  • <video><audio>嵌入多媒体资源。
  • <object>, <embed><applet>的插件。
  • @font-face引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。
  • <frame><iframe>载入的任何资源。站点可以使用X-Frame-Options消息头来阻止这种形式的跨域交互。

一个经久不衰的BUG
早在2011年,一个用户在Mozilla的Bug追踪系统中就提交了一个issue,声称他可以判定某个网站的访客是否登录了gmail,facebook等等。

嵌入iframe来获取一个访问网站的用户是否登陆了gmail:

1
2
3
4
5
<img style="display:none;"
onload="logged_in_to_gmail()"
onerror="not_logged_in_to_gmail()"
src="https://mail.google.com/mail/photos/img/photos/public/AIbEiAIAAABDCKa_hYq24u2WUyILdmNhcmRfcGhvdG8qKDI1ODFkOGViM2I5ZjUwZmZlYjE3MzQ2YmQyMjAzMjFlZTU3NjEzOTYwAZwSCm_MMUDjh599IgoA2muEmEZD"
/>

src的代码试图访问一张gmail中攻击者上传的图片,如果用户没有登陆gmail,就无法成功加载。从而达到判断用户是否登陆gmail的效果。这种方法可以推广到任何对不应跨源访问的资源没有正确设置同源策略的网站。

跨域的解决方案

document.domain跨子域

常见于不同子域共享数据

如果两个window或者frames包含的脚本可以把domain设置成一样的值,那么就可以规避同源策略,每个window之间可以互相沟通。例如,orders.example.com下页面的脚本和catalog.example.com下页面的脚本可以设置他们的document.domain属性为example.com,从而让这两个站点下面的文档看起来像在同源下,然后就可以让每个文档读取另一个文档的属性。

这种方式也不是一直都有用,因为端口号是在内部保存的,有可能被保存成null。换句话说,example.com的端口号80,在我们更新document.domain属性的时候可能会变成null。为null的端口可能不被认为是80,这主要依赖浏览器实现。

# CORS 跨域资源共享

CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)

这种方式使用了一个新的Origin请求头和一个新的Access-Control-Allow-Origin响应头扩展了HTTP。允许服务端设置Access-Control-Allow-Origin头标识哪些站点可以请求文件,或者设置Access-Control-Allow-Origin头为*,允许任意站点访问文件。浏览器,例如Firefox3.5,Safari4,IE10使用这个头允许跨域HTTP请求。

# jsonp

详见JSONP利用与防御文章

# 服务器代理

代理是万能的,什么都不影响了

# 使用postMessage实现页面之间通信

信息传递除了客户端与服务器之前的传递,还存在以下几个问题:

  • 页面和新开的窗口的数据交互。
  • 多窗口之间的数据交互。
  • 页面与所嵌套的iframe之间的信息传递。

window.postMessage是一个HTML5的api,允许两个窗口之间进行跨域发送消息。这个应该就是以后解决dom跨域通用方法了,具体可以参照MDN。

#WebSocket

现代浏览器允许脚本直连一个WebSocket地址而不管同源策略。然而,使用WebSocket URI的时候,在请求中插入Origin头就可以标识脚本请求的源。为了确保跨站安全,WebSocket服务器必须根据允许接受请求的白名单中的源列表比较头数据。

对同源策略的利用

URI解析

IPURI的重要组成部分,如果留心了RFC的人就会知道,IP不止有一种格式。下面的标注形式其实都代表了同一个IP:216.58.209.68

1
2
3
4
216.58.53572
0xD8.072.53572
3627733316
0330.3854660

当然,不同浏览器对URI的解析方式是不一样的,可能并不能正确跳转的google的页面,但当浏览器URI解析存在漏洞时,就会有作用。

1
2
3
4
5
当某些浏览器对URI的解释存在漏洞的时候,就可以构造出有趣的攻击链来绕过SOP。 比如CVE-2015-7188火狐浏览器SOP绕过中。攻击者构造了特殊的URL,并在攻击者自己控制的来自37.187.18.85的网页中发起跨域请求
http://37.187.18.85BuFF20translate.google.com/fx_sop_bypass/FlashTest.swf?url=http://translate.google.com/manager/website/
先通过B让Firefox认为这个请求是请求37.187.18.85本身的内容,再通过类似@字符的Unicode字符@(uFF20)让浏览器认为@之前的字符都是translate.google.com的账号和密码,从而返回translate.google.com的网页内容,实现绕过SOP。

设计缺陷导致SOP绕过

在Java6,7中,如果两个域名解析到相同的IP,则会认为他们同源。假设我们有attacker.com和victim.com,两者都共享主机123.123.123.123。攻击者attacker.com可以在自己控制的域名下上传一个jar文件来访问victim.com的内容。

访问本地文件的同源策略

不同的浏览器使用不同的浏览器引擎,而不同的引擎对于同源策略的处理也并非完全一致。

1
2
3
4
5
6
7
<!-- 文件路径:/home/user/1.html -->
<html>
<frameset cols="50%,*">
<frame src="/home/user/dir/2.html">
<frame src="...">
</frameset>
</html>

文件1与2在不同的浏览器引擎中可能认为是同源/非同源

IE的特别之处

TrustZones(信任域):当一个URI被加入到了IE的信任网站区域中时,浏览器会无视同源策略。

IE在考虑同源策略时不包括端口, 这意味着不同端口上的应用程序可以读取到比如用户的登陆账户密码/cookie等。

通过变更自身的源绕过同源策略:
IE 6,7版中网页可以通过document.domain设置自身的来源为任意其他来源。如今网页仍然可以更改源,但是有一些限制。

网页可以变更自身的源为父级域名。

例如http://malicious.eth.space/1.html可以通过执行

1
document.domain = "eth.space";

来绕过同源策略的限制,从而可以读取http://eth.space/login.html上的内容。这其中的应用大家可以自己去想。

防御

CORS cross-origin sharing stander 跨源资源共享机制

使用windows.postMessage
使用JSONP

CSP 内容安全策略

禁止(你的资源被)跨源访问

为了禁止跨域写,我们需要引入CSRF令牌,然而我们需要正确的配置同源策略,否则CSRF令牌本身也将被恶意网页读取。

为了禁止跨域读,我们可以通过设置X-Frame-Options头来禁止该页面被嵌入到恶意页面中,就如同在“经久不衰的BUG”中一样。

为了禁止跨域嵌入,确保你的资源本身无法嵌入到各种跨域访问方式中,比如<script data-original=></script> <img data-original=x></img>,<svg onload=>,各种字体加载等等。同时,使用CSRF令牌也可以有效避免被跨域嵌入。