追記
- 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の役割」 + 「Refererheader の除外」がnoreferrerの役割
noreferrerがnoopenerと一緒に使われるのは、古いブラウザでwindow.openerをundefinedにするため- というのも、
window.openerが問題視され始めた当時は、noopenerをサポートしていたブラウザには限りがあったため - 一方
noreferrerはnoopenerよりも古くからあるオプションで、 IE11 でもサポートされている (ただし win10 の IE11 のみ / win7〜8.1 の IE11 ではサポートしてない) - そのため、
noopenerの fallback として、noreferrerが使われることが多かったはず (はず、というのは僕が当時の Web の状況に詳しくないから)
- というのも、
- しかし
noreferrerには副作用がありますRefererheader が無くなるので、リンク先からどこからユーザが流入してきたのか追えなくなってしまう- アクセス分析をする時に困るかも
- 一応回避策はあることにはある
?utm_campaign=...みたいなクエリを URL につけておくとか- けどこういうので全部カバーしきれるかは分かっていない
- アクセス分析が重要なサービスでは安全側 (ユーザではなくアクセス分析する我々にとっての安全側) に倒して付けないほうが良いかも?
- ユーザの安全と我々の安全を天秤にかける、みたいな
今も noreferrer を付けるべきなのか
- アクセス分析をガリガリやっていきたいとかなら付けないほうが良さそう?
- けど
Refererheader があることでどれくらいできることが広がるのか、というのはよく分かっていない 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 の進化に置いてかれます。