target="_blank"を使った脆弱性Tabnabbingとその対策

About rel=noopenerの記事を見て、フィッシング詐欺の手法Tabnabbing(タブナビング)について、
以前にそれっぽい記事読んだけど、すっかり忘れていた。

改めて見直してみて、その内容と対策がまとまっていたのでおさらい。

Tabnabbing(タブナビング)の仕組み

Tabnabbing の仕組みは以下となる。
まず、新規のタブを開くようなアンカータグ

1
<a href="https://example.com" target="_blank">example.com</a>

またはjavascript

1
window.open()

が存在してるページが存在している。

このようなhtmlの記述やjavascriptがある場合に攻撃の対象となりうる。
というのも、上記のリンクで開かれたページ内から以下のようにwindow.openerを操作できるからである。

1
window.opener.location = 'http://phishing.example.com';

上記のスクリプトが実行されると、元のタブページをフィッシングサイトなどへの誘導が可能となる。
ユーザーはこの挙動に気がつくにはリンクが開かれる度に

  • tabの変更を注視する(ローディングされているとか、fav.iconが変わっているとか)
  • ブラウザの履歴を調べる
  • セッションなどを使っている際にはログアウトしたりすることをきにする
  • urlや証明書の情報が異なることをチェックする

多くの場合は、一般的に気をつけていないと分かりにくい。

対策

ブラウザごとにtarget=”_blank”でタブを開く際の操作と影響を受けるブラウザのリストは以下のようになる。

ブラウザ クリック Shift+クリック Ctr+クリック
Chrome 40 x x x
Firefox 34
Opera 26 x x x
Safari 7,8 x
IE 6 - 11
(blankshield より引用)

Internet Explore(IE)はユーザーのセキュリティゾーンの設定次第ではその影響を受けることになる。

target=”_blank”を使う場合

開発者としてtarget=”_blank”を使う場合は、rel=”noopener”を使い、新たに開いたタブから元のタブのlocationを変更できないようする必要がある。

しかし、noopenerは現在のところブラウザのサポート状況によっては使用できない。(Firefoxなど)

そのため、併せてrel=”noreferrer”を追加することで新たに開いたタブでのwindow.openerはnullとなりTabnabbingは回避できる。
(ただしnoreferrerはリファラを送らないので、その辺も考慮して実装する必要はありそう。)

最終的には以下のようになるとよい。

1
<a href="https://example.com" target="_blank" rel="noopener noreferrer">example.com</a>

ちなみに、rel=”noreferrer” を使うことはパフォーマンス的にも利点がある。
通常のtarget=”_blank”で開いた場合は、同プロセス(スレッド)で処理が行われるため元のタブのパフォーマンスが低下する。
Chromiumブラウザにおいて、noreferrerの場合は新しいプロセスでタブを開くため、元のタブのパフォーマンスが低下することはない。

windw.open()を使う場合

windwo.open()で開く場合は、クリックイベントでデフォルトのクリックイベントをキャンセルする。
その後、非表示iframe内でwindowを開き、その結果を返すことでwindow.openerをnullにできる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<a>github.com</a>
<script>
var targets = document.querySelectorAll('a[target=_blank]');
var openWindow = function(url) {
var iframe, iframeDoc, script;
iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);

iframeDoc = iframe.contentDocument || iframe.contentWindow.document;

script = iframeDoc.createElement('script');
script.text = 'window.parent = null; window.top = null; window.frameElement = null; var child = window.open("' + url + '"); child.opener = null';

iframeDoc.body.appendChild(script);
newWin = iframe.contentWindow.child;

document.body.removeChild(iframe);

return newWin;
}

for(var i = 0; i < targets.length; i++) {
targets[i].addEventListener('click', function(event) {
var url = event.target.getAttribute('href');
// window.openを置き換える
// window.open(url);
openWindow(url);
event.preventDefault();
});
}
</script>

上記の2つの対策はblankshieldを利用することで実現できる。

試してみる

ということで、上記の対策を実現するTabnabbingのDEMOを作成した

まとめ

ログインなどの機能を有するページにおいて、target=”_blank” を使うことでフィッシング攻撃の手段を提供することにもなる。
リンク先のドキュメントは完全に操作はできないため、開発者はtarget=”_blank”を用いるべきかの正しい判断、その取扱いと適切な処理を知っておく必要がある。

UXの観点から、target=”_blank”を使わないというのも一つの手段としては考えられる。

参考にしたページ

About rel=noopener
Bugzilla#1094449
blankshield
The performance benefits of rel=noopener
Phishing by navigating browser tabs

Comments