在网页中使用链接时,如果想要在新的标签页打开指定的地址,通常的做法就是在 标签上添加
target="_blank"
属性,然而,就是这个属性,为钓鱼攻击者带来了可乘之机。
本文来聊聊 target="_blank"
会引发钓鱼风险的原理,以及如何防范此类钓鱼攻击。
原理
我们知道,当在 JavaScript
中调用 window
对象的 open()
方法打开一个新窗口时,可以获得一个创建窗口的 opener
句柄,但你也许没有注意到,当使用 超级链接标签时,如果使用
target="_blank"
在新窗口或标签页中打开资源,子窗口也能捕获到 opener
句柄。
通过 opener
句柄,在子窗口中可以访问到父窗口的一些属性,比如可以修改父窗口的地址信息,让父窗口跳转到指定的页面,虽然所能访问到的属性有限,却为有心人带来可乘之机,利用其实现钓鱼攻击。
举个栗子
假如在页面 a.html
中布局如下:
<form action="/login" method="post"> <div><input type="text" placeholder="用户名" />div> <div><input type="password" placeholder="密码" />div> <div><input type="submit" value="登录" />div>form><a href="/b.html" target="_blank">跳转到b.html页面a>
页面中有一个链接,点击后打开新标签页并跳转到 b.html
页面,b.html
页面代码片段如下:
<div>切换到前一页面看看效果div><script> if (window.opener) { window.opener.location = '/c.html' }script>
在页面 b.html
被打开时, 中脚本被执行,原来打开的
a.html
标签页会被重定向到 c.html
, c.html
可以是和原来域相同或完全不相关的其它域的资源。
在 c.html
中伪装 a.html
页面中的表单:
<form action="/another_login.do" method="post"> <div><input type="text" placeholder="用户名" />div> <div><input type="password" placeholder="密码" />div> <div><input type="submit" value="登录" />div>form><b>这是钓鱼伪装的页面b>
效果如下图所示:
隐患
既然可以通过打开的子窗口获得 opener
句柄来修改父窗口的 location
,让父窗口跳转到指定的页面,那么钓鱼网站就可以利用这个“漏洞”来实现钓鱼攻击:
- 攻击者在论坛发布帖子或回复帖子,其中带有外链,并诱导用户点击链接
- 某访问者点击了这个外链,打开新窗口或标签页
- 切换回原来的站点时,它已经被换成了一个跟原站点界面一模一样的钓鱼网站,并且这个钓鱼网站提示你登录信息已过期,需要重新输入用户名和密码
- 访问者认为真的是登录信息过期了,重新输入了用户名与密码
- 钓鱼网站得到用户名和密码,重定向到真正的网站,由于真正网站的
cookie
或web storage
等信息还在(保存了用户登录信息),所以也直接就登录进去了 - 访问者认为正常登录成功,但事实上,账号信息已经不知不觉被盗了
当然这个攻击也有破绽,比如钓鱼网站和真实网站的域名会不一样,但通常访问者在浏览网站时,不会去观察网站的域名,而且很多钓鱼网站会让域名跟真实网站的域名非常相似以混淆视听,比如 example.com
和 examp1e.com
,不仔细分辨还真以为是同一个呢。
防范
由于可以使用 标签打开新窗口,也可以使用
window.open()
来打开新窗口,所以防范处理如下:
针对
标签 target="_blank"
的防范
可以在所有使用了 target="_blank"
的链接上添加 rel="noopener noreferrer"
属性来防范钓鱼攻击。
某些旧版本 Firefox
、Edge
、IE
并不支持 rel="noopener"
的使用:
为了兼容老版本浏览器,可以使用 rel="noreferrer"
来防范:
可以合并这两种写法:
<a href="/b.html" target="_blank" rel="noopener noreferrer"> 跳转到b.html页面a>
但需要注意的是,IE11
之前的浏览器仍不支持使用这种使用,如果需要兼容老版本的 IE
,可参考 http://danielstjules.github.io/blankshield,但通常默认情况下,IE
不会受到攻击。
针对 window.open()
方法的防范
可以利用 JavaScript
中的 window.open()
来实现防范:
<a href="/b.html" target="_blank" onclick=" var otherWindow = window.open(); otherWindow.opener = null; otherWindow.location = href; return false; "> 跳转到b.html页面a>
换种写法:
<a href="/b.html" target="_blank" class="link"> 跳转到b.html页面a><script> const link = document.querySelector('.link') link.addEventListener('click', (e) => { e.preventDefault() const otherWindow = window.open() otherWindow.opener = null otherWindow.location = e.target.href }, false)script>
这样在子窗口中就无法获取父窗口的 opener
句柄了。