追記
- 2020/10/29: モダンブラウザによって
noreferrer
が自動的に付与されつつある、という説明をしていましたが、正しくはnoopener
の間違いでした。既に記事の方は修正済みです。
追記終わり。
target="_blank"
は新しいタブでリンク先を開くためのオプションです- しかし、これにはリンク先のページからリンク元のページの
window
オブジェクトにアクセスできるという、ちょっと癖のある挙動があります - この挙動自体は脆弱性ではありませんが、 フィッシング詐欺などに悪用される可能性がある、危険な挙動であることが知られています
- リンクのへの rel=noopener 付与による Tabnabbing 対策 | blog.jxck.io
noopener
- そこで一般には、
target="_blank"
な<a>
タグにはnoopener
を指定することが推奨されています- リンクのへの rel=noopener 付与による Tabnabbing 対策 | blog.jxck.io
- Links to cross-origin destinations are unsafe | Lighthouse | Chrome for Developers
- これを付けると、リンク先のページからリンク元のページの
window
にアクセスできなくなります (window.opener
がundefined
になる)
- また、このオプションは Spectre 対策としても利用されます
- Site Isolation 及び Web のセキュリティモデルの更新 | blog.jxck.io
- 今まではリンク先のページからリンク元のページへの参照があるために、互いにメモリを共有する必要があり、どうしても同じプロセスで実行しなければならず、結果として Spectre の影響を受ける可能性がありました *1
noopener
を付けると互いのページが別々のプロセスで実行されるようになり、リンク先のページからリンク元のページのメモリ上のデータを推測することが不可能になります
- つまり「リンク元のページの
window
オブジェクトの保護」 + 「リンク元のページのメモリ上に展開されているデータの保護」がnoopener
の役割
余談: どういう時に noopener を付けるか
- じゃあ
target="_blank"
な<a>
タグ全てに問答無用で付けなければならないのか、という疑問が当然出てくると思いますが、実はそうでもないです - リンク元のページの
window
オブジェクトの保護や、リンク元のページのメモリ上に展開されているデータの保護が不要な時はなくても問題ありません - 例えば以下のようなケースでは付けなくても問題ないです
- リンク先が same-origin なページの場合
- 悪意のある JS から
window.opener
を参照されるのは困るけど、same-origin なら所有者は我々であり、悪意のあるコードはないはずなので、window.opener
を参照されても問題ない、という発想 - 同様の理由でメモリを覗き込まれても問題ないはず
- 悪意のある JS から
- リンク先から
window.opener
をどうしても参照したい時window.opener
使いたいんだったらnoopener
付けたら駄目でしょ、という当たり前の話- けどこれからの Web は Site Isolation をどんどん推し進めていく方向に発展すると思われるので、可能な限り
window.opener
を使わないほうが良いです - 代わりに
MessageChannel
という代替技術を使うのが推奨されています- Origin をまたぐその他の仕様|Origin 解体新書 v1.5.1 (有料だけど良い資料です)
- リンク先が same-origin なページの場合
- とはいえいちいち same-origin かどうかを判定するのは面倒
- なので基本的には
target="_blank"
な<a>
タグ全てにnoopener
を付けておく、という規約を導入するというのが個人的にはオススメです- 別に付けても何ら問題ではない / 付けておくだけ得 / 思考の手間が無くなる
- うっかり
noopener
付いていない<a>
タグをコピペしてきて、 フィッシング詐欺に発展してしまう、みたいなことも防げる
- あとは COOP を有効にするのも手
- Site Isolation 及び Web のセキュリティモデルの更新 | blog.jxck.io
noopener
いちいち付けなくても、same-origin 以外ではwindow.opener
を制限する、みたいなことができる- 人間が付け忘れているかどうかに気を配る必要が無くなって良いですね
- なので基本的には
余談: 自動的な noopener の付与
- 近年ユーザ保護の一環で、
target="_blank"
な<a>
タグにブラウザがデフォルトでnoopener
を付与するようになっています - 「Firefox 79」からtarget=“_blank”なリンクの挙動が変更、より安全な仕様に - 窓の杜
- 既に Firefox、Safari では実施済み / Chrome (+Edge) も追随予定
- つまり
noopener
を付けていくのが当たり前になりつつある訳ですね
noreferrer
- 同じく
target="_blank"
な<a>
タグに指定できるオプションとしてnoreferrer
というものがあり、これもnoopener
と合わせて使われることがあります- Referrer-Policy によるリファラ制御 | blog.jxck.io
noreferrer
は<a>
タグのリンク先からリンク元の情報を取得できないよう制限するオプションです- これを付けると、
<a>
タグのリンク先に遷移する際に、遷移元の URL がリクエストのReferer
ヘッダに載らなくなります - また、
window.opener
から遷移元の情報が取れてしまうので、noreferrer
を付けるとこれもundefined
になります - つまり 「
noopener
の役割」 + 「Referer
header の除外」がnoreferrer
の役割
noreferrer
がnoopener
と一緒に使われるのは、古いブラウザでwindow.opener
をundefined
にするため- というのも、
window.opener
が問題視され始めた当時は、noopener
をサポートしていたブラウザには限りがあったため - 一方
noreferrer
はnoopener
よりも古くからあるオプションで、 IE11 でもサポートされている (ただし win10 の IE11 のみ / win7〜8.1 の IE11 ではサポートしてない) - そのため、
noopener
の fallback として、noreferrer
が使われることが多かったはず (はず、というのは僕が当時の Web の状況に詳しくないから)
- というのも、
- しかし
noreferrer
には副作用がありますReferer
header が無くなるので、リンク先からどこからユーザが流入してきたのか追えなくなってしまう- アクセス分析をする時に困るかも
- 一応回避策はあることにはある
?utm_campaign=...
みたいなクエリを URL につけておくとか- けどこういうので全部カバーしきれるかは分かっていない
- アクセス分析が重要なサービスでは安全側 (ユーザではなくアクセス分析する我々にとっての安全側) に倒して付けないほうが良いかも?
- ユーザの安全と我々の安全を天秤にかける、みたいな
今も noreferrer を付けるべきなのか
- アクセス分析をガリガリやっていきたいとかなら付けないほうが良さそう?
- けど
Referer
header があることでどれくらいできることが広がるのか、というのはよく分かっていない noreferrer
を付けないと IE11 ユーザを危険な状態に晒すことになるので、慎重に検討しましょう
- けど
- リンク元の URL が外部に流出してほしくないなら付けたほうが良さそう
- 社内限定のサービスとか
- YouTube の限定公開 URL とか
- 単に
noopener
の fallback として使いたい場合...- これは結構悩ましい。サービスによって判断すると良いのではと思ってます
- IE11 以外のモダンブラウザでは
noopener
サポートされているので、IE11 サポートしていないサービスではnoreferrer
付けなくて良いはず - 一方 IE11 をサポートするブラウザでは
noreferrer
を付けておくと安全- ただしアクセス分析をする時に困る可能性があるので、そこは頑張って天秤にかける、という形になりそう
- 悩ましい!!!
- この辺詳しい人に聞いてみたい
*1:では noopener 付けていないページは今危険な状態に晒されているのか、というとそうでもないです。というのも、Spectre を成立させるにはメモリを覗きたいページが同一プロセスに載っていること、高精度のタイマー API が利用可能であること、という 2 つの条件がどちらも成り立っていなければなりません。Spectre が発見されてすぐにブラウザはこの後者の API を JS で無効にしていて、原理的に Spectre を成立しないようにしています。じゃあ Spectre 成立しないなら noopener 付けなくて良いじゃん、というとそうでもなくて、window.opener があることで高精度のタイマー API をブラウザが提供することができなくなってしまいます。他にも、window.opener のセキュリティ上の懸念で実装できない API が今後出てくるかもしれません。そうした API をブロックしてしまわないよう、noopener を付けていく / window.opener を無効にする、というのが今後ますます重要になってきます。要は付けないと Web の進化に置いてかれます。