在一個新窗口中打開鏈接是前端開發中一個很常見的邏輯,它可以將用戶引導到一個新的域名。我們可以用target='_blank'
來實現這個功能。我敢肯定,每個人都會在他的某個項目中使用過target='_blank
,但是我不確定是否每個人都知道這種用法的缺陷。
當一個外部鏈接使用了target=_blank
的方式,這個外部鏈接會打開一個新的瀏覽器tab。此時,新頁面會打開,并且和原始頁面占用同一個進程。這也意味著,如果這個新頁面有任何性能上的問題,比如有一個很高的加載時間,這也將會影響到原始頁面的表現。如果你打開的是一個同域的頁面,那么你將可以在新頁面訪問到原始頁面的所有內容,包括document
對象(window.opener.document
)。如果你打開的是一個跨域的頁面,你雖然無法訪問到document
,但是你依然可以訪問到location
對象。
這意味著,如果你在你的站點或者文章中,嵌入了通過新窗口打開一個新頁面的鏈接,這個新頁面可以使用window.opener
,在一定程度上來修改原始頁面。
可以參考這個例子:s.codepen.io/adamlaki/de…
(筆者這里做了一個小gif,方便大家看上面那個例子的效果)
<figcaption></figcaption>
我們來看看上面例子發生了什么?當你點擊了鏈接(在打開的document
中),瀏覽器會打開這個頁面。而這個頁面中運行了一段JavaScript
代碼:通過window.opener
來修改原始頁面(你來自的那個頁面)。有點乏味但是這可能是有害的。
<figcaption></figcaption>
那么問題來了:我們如何阻止這種情況的發生呢?在所有使用target=_blank
打開新頁面的鏈接上,加上ref="noopener"
。
<a rel="noopener"></a>
復制代碼
使用了ref=noopener
以后,當一個新頁面通過一個鏈接打開后,新頁面中的惡意JavaScript
代碼將無法通過window.opener
來訪問到原始頁面。這將保證新頁面運行在一個單獨的進程里。
在老瀏覽器中,你可以使用ref=noreferrer
屬性,具有同樣的效果。但是,這樣也會阻止Referer
header被發送到新頁面。
<a rel="noopener noreferrer"></a>
復制代碼
在上面的例子中,使用了rel="noreferrer"
,當一個用戶點擊了這個超鏈接進入到新頁面后,新頁面拿不到referrer
信息。這將意味著,新頁面不知道用戶是從哪里來的。
如果你通過JavaScript
中的window.open
打開一個頁面的話,上文所說的都適用,因為你也是打開了一個新的窗口。在這種情況下,你不得不清楚掉opener
對象:
var newWindow = window.open();
newWindow.opener = null;
復制代碼
在我看來,使用第一種解決方案(在每一個target="_blank"
的鏈接中加上ref="noopener"
)是沒有什么明顯的壞處的。這個問題表明,在你的網頁安全性中找到漏洞是多么的容易。
筆者的總結
這是一篇很短的文章,主要介紹了在使用<a target="_blank">
標簽打開一個新窗口過程中的安全問題。新頁面中可以使用window.opener
來控制原始頁面。如果新老頁面同域,那么在新頁面中可以任意操作原始頁面。如果是不同域,新頁面中依然可以通過window.opener.location
,訪問到原始頁面的location
對象。
試想一下,你在自己的a頁面中,通過<a target="_blank" >
打開新窗口,跳轉到了b頁面,此刻b頁面中有一段代碼window.opener.location = 'http://c.com'
。這是,a頁面就會自動跳轉到c頁面。如果這個c頁面是一個和a頁面長得一樣的釣魚網站,那么用戶可能就中招了。
解決方法就是:在帶有target="_blank"
的<a>
標簽中,加上rel="noopener"
屬性。如果使用window.open
的方式打開頁面,將opener
對象置為空。這樣的副作用是:在某些低版本瀏覽器中,新頁面中拿不到referer
信息。
寫在后面
本文介紹了一種前端開發中容易引發安全問題的情況,問題不大,但是比較容易被忽略。筆者自己也是第一次接觸到這個問題 - -。
符合預期。
轉載自:CoyPan
鏈接:https://juejin.im/post/5c36d52651882525e90dc800
來源:掘金