❗ 本文最后更新于 3348 天前,文中所描述的信息可能已发生改变,请谨慎使用。
上篇文章中我介绍了 Content Security Policy Level 2,之前我还介绍过 Subresource Integrity、Referrer Policy 等其它与 Web 安全或用户隐私有关的协议。由于精力有限,我所写的只是新增协议中很小的一部分,但已经可以由此感受到现代浏览器在安全上所作出的努力。然而,一个我认为比较严重的安全隐患,却始终存在于各大浏览器之中。甚至到目前为止,仍然没有一个正式标准出来指导浏览器和开发者完美解决这个问题。这让我十分困惑,我将在本文讨论这个问题。
在浏览器中,通过 <a>
标签或者 JavaScript 中的 window.open
函数,可以打开新页面。新页面的 window 对象中,存在一个 opener
属性,保存对父页面的引用。我们知道,Web 应用的安全性,很大程度上是由同源策略(Same Origin Policy,SOP)所保证的。但是,在子页面访问 opener.location
的一些属性和方法时却不受 SOP 保护,这就是本文要探讨问题的核心所在。
来看一个案例,假设父页面中有新窗口打开的子页面链接:
<a href="https://qgy18.mailseason.com/file/opener.html" target="_blank">click me</a>
opener.html
中有这样一段代码:
<script>
window.opener.location = 'https://mailseason.com/post/about.html';
//window.opener.location.replace('https://mailseason.com/post/about.html');
</script>
将以上两段代码,分别生成两个不同域的页面(本文探讨安全风险,故只考虑不同域的情况)。在大部分浏览器中,通过父页面中的链接打开子页面后,子页面都可以通过 opener.location
将父页面跳走(上面两行 JS 可以都可以跳转,不同之处是 replace
不产生历史纪录)。
现在很多社区允许用户填写个人网站链接。设想一下,你点开某人资料中的链接,浏览一番后关掉新窗口,如果原来的页面已经被重定向到高仿的钓鱼页,你会轻易察觉出来么?
这个现象,很早之前就被人发现并利用在黑帽 SEO 上,同样很早之前,就有人给各大浏览器提 bug(详情),得到的建议无外乎两种:1)通过 window.open 打开链接,并将 opener 置为空;2)通过给链接加上 rel=noreferrer 属性,将 opener 置为空。
我们测试一下这两种方案是否能达到预期效果,以及是否会带来负面影响。加上默认情况,一共要测试三种情况,代码如下:
<a href="https://qgy18.mailseason.com/file/opener.html" target="_blank">click me</a>
<a href="https://qgy18.mailseason.com/file/opener.html" target="_blank" onclick="var win=window.open(this.href,'_blank');win.opener=null;return false;">click me</a>
<a href="https://qgy18.mailseason.com/file/opener.html" target="_blank" rel="noreferrer">click me</a>
以下是我在部分浏览器下的测试结果:
浏览器 | 1)默认情况 | 2)window.opener=null | 3)rel=noreferrer |
---|---|---|---|
IE 8.0.6001.18702 | 不跳转 *,有 Referrer | 不跳转,无 Referrer | 不跳转 *,有 Referrer |
IE 11.0.10240.16431 | 跳转,有 Referrer | 不跳转,无 Referrer | 跳转,有 Referrer |
Edge 20.10240.16384.0 | 跳转,有 Referrer | 不跳转,无 Referrer | 跳转,有 Referrer |
Chrome 45.0.2454.101 | 跳转,有 Referrer | 不跳转,有 Referrer | 不跳转,无 Referrer |
Firefox 41.0.1 | 跳转,有 Referrer | 不跳转,有 Referrer | 不跳转,无 Referrer |
Safari 9.0.1 | 跳转,有 Referrer | 跳转,有 Referrer | 不跳转,无 Referrer |
(注:IE 8.0 中,方案 1 和 3 默认不会跳走,但会有弹出窗口被拦截的提示。这个问题可以通过在页面增加 var location;
来解决,不属于本文重点,这里不展开讨论)
由表格可以看出,在所有现代浏览器中,默认情况下父页面都会被跳走。方案 1,在最新的 Safari 下不能阻止跳转,并且会导致 IE 系列丢失 Referrer;方案 2,在不支持 rel=noreferrer 的 IE 中等同于默认情况,在其它浏览器中可以阻止跳转,同时 Referrer 也被去掉了。
这两个方案都不完美,Referrer 在很多时候并不能轻易去掉,这样只剩下 window.open 这个「改动成本大、不优雅、会引入新的问题」的方案勉强可用了。
于是,一些人开始提出各种建议,试图让浏览器既能保留 Referrer,又能阻断 opener 引用。下面是一些提议,可惜到目前为止并没有任何浏览器采纳:
- rel="newcontext":建议给 rel 属性增加
newcontext
属性值,详情1、详情2; - rel="unrelated":建议给 rel 属性增加
unrelated
属性值,详情; - target="_unrelated":建议给 target 属性增加
_unrelated
属性值,详情; - disown-window-opener:建议在 CSP3 中增加
disown-window-opener
指令,详情;
2016-05-11 更新,目前有一个新的 rel
属性来解决这个问题:
rel=noopener, Ensure new browsing contexts are opened without a useful window.opener
给 A 链接加上这个属性,打开的新页面再也无法通过 window.opener
获取父页面,同时 Referer 不受影响,可以看作这个问题的终极解决方案了。这个属性更多描述详见 HTML Standard;浏览器支持情况详见 CanIUse,当前只有 Chrome 49+ 支持。
到这里为止,我们讨论的都是「新窗口打开的子页面将父页面跳走」所带来的风险。实际上,父页面也可以将子页面跳走,这也是一个风险点。假设我的网站上有一个名为「XX 网站登录」的外链,用户点击后发现打开的确实是 XX 网站登录页,正准备输入密码时父页面将这个子页面跳转到钓鱼页面,也不容易被察觉。为了避免加载时的空白,还可以将钓鱼页以 data URIs 的形式编码,事先准备好。
下面是一个简单的案例:
<a id="link" href="#" target="_blank">点击打开 XXX 网站</a>
<script>
document.getElementById('link').addEventListener('click', function(e) {
e.preventDefault();
var win = window.open('https://qgy18.mailseason.com/file/login.html', '_blank');
setTimeout(function() {
win.location.replace('data:text/html;charset=utf-8,<!DOCTYPE%20html><html><head><meta%20charset%3D"utf-8"%20%2F><%2Fhead><body><div>这是虚假的登录页面:<br><br><input><%2Fdiv><%2Fbody><%2Fhtml>');
}, 3000);
});
</script>
点击链接后打开的确实是正常的登录页,但几秒后会被替换为提前准备好的钓鱼页,如果这时没注意地址栏的变化,就很容易被钓鱼者利用。
本文先写到这里,这个问题我会持续关注,如果有浏览器提供了更好的解决方案或者有新的标准规范出来,我会及时更新本文。也欢迎大家留言讨论。
本文链接:https://mailseason.com/post/the-security-of-window-opener-location.html,参与评论 »
--EOF--
发表于 2015-10-09 01:09:00,并被添加「Web安全」标签。查看本文 Markdown 版本 »
专题「浏览器」的其他文章 »
- iOS 10 Safari 视频播放新政策 (Oct 07, 2016)
- Chrome 中 scrollingElement 的变化 (Apr 16, 2016)
- 域名小知识:Public Suffix List (Nov 28, 2015)
- 使用 SRI 增强 localStorage 代码安全 (Sep 26, 2015)
- Subresource Integrity 介绍 (Sep 23, 2015)
- 移动 Web 与 JavaScript 定时器 (Mar 27, 2014)
- Chrome 和 Web Fonts 二三事 (Mar 24, 2014)
- Webkit 异步加载 CSS 的奇怪现象 (Dec 25, 2013)
- 小成本实现部分选中的复选框 (Dec 22, 2013)
- Chrome 滚动条冻结现象 (Dec 02, 2013)
Comments
Waline 评论加载中...