摘要
一般来说,打开同源(域名相同)的页面,不会有什么问题,毕竟都是一个站点提供的。但对于跨域的外部链接来说,存在一个被钓鱼的风险。 在跨域的情况下,window.opener 拿不到来源页面的具体内容,但是 window.opener.location 却是例外:它可以修改打开它的页面地址。这会带来什么问题?如果你在社交网站上点击了一个钓鱼链接,钓鱼页面本身没有问题,但它却可以悄悄地替换打开它的社交网站页面,从而实现钓鱼以获取用户账号密码等信息的目的。
1. 什么是 window.opener
window.opener
表示打开当前窗体页面的的父窗体是谁。
例如,在 A 页面中,通过一个带有 target="_blank"
的 a
标签打开了一个新的页面 B,那么在 B 页面里,window.opener
的值为 A 页面的 window 对象。
无来源的打开的页面,或者被禁止了 opener 的页面创建/打开,opener 的值为 null
。
2. window.opener 的安全问题
一般来说,打开同源(域名相同)的页面,不会有什么问题。但对于跨域的外部链接来说,存在一个被钓鱼的风险。
在跨域的情况下,window.opener 拿不到来源页面的具体内容,但是 window.opener.location
却是例外。它会带来什么问题呢?
举个栗子:
假如你在新浪微博首页设置了个人主页链接(例如: https://lzw.me),通过访问你的微博首页点击了这个链接,正常情况下会打开一个新页面,新页面自然是 https://lzw.me(志文工作室的首页)。
如果微博没有对这个主页链接设置禁用 window.opener 的跟踪,在打开的 https://lzw.me 页面,可以通过 window.opener.location
改写来源站点的地址。利用这一点,将来源站点改写到钓鱼站点页面上,例如跳转到伪造的微博登陆页面,这个时候是很难被用户发现的,那么你的账号就存在被钓鱼走的可能了。
下面以新浪微博作为演示。
A. 点击微博里的外链:
B. 在打开外链页面中,可以获取到 window.opener
的值。由于跨域,这里获取到的窗体句柄是有限的。
C. 通过修改 window.opener.location
,篡改了来源页面。如果篡改的页面是一个伪造的微博主页,那么就可以愉快的钓鱼了
3. 如何禁用 window.opener
3.1 设置 rel 属性
示例:
<a href="https://lzw.me" target="_blank" rel="noopener" rel="noreferrer">志文工作室</a>
rel=noopener
规定禁止新页面传递 rel=noopener,通过设置了此属性的链接打开的页面,其 window.opener 的值为 null。
但该属性在 chrome 49+,Opera 36+ 版本中才得到支持。
在不支持 rel=noopener
属性的浏览器中,可以使用 rel=noreferrer
禁用 HTTP 头部的 Referer 属性跟踪来源页面,得到的效果一样。
3.2 widow.open 方式
对于使用 widow.open
脚本打开的新页面,由于可以得到新页面的窗体句柄,那么可以改写它的 opener 值。示例:
var newWin = window.open('https://lzw.me');
newWin.opener = null;
当然也可以这样改写所有 a
标签的行为:
$(documnet).on('click', 'a', function(e) {
var href = $(this).prop('href'),
newWin;
// 非同源链接
if (/^http(s)/i.test(href) && !href.indexOf(location.hostname)) {
e.preventDefault();
newWin = window.open(href);
newWin.opener = null;
}
});