mizdra's blog

ぽよぐらみんぐ

target=''_blank" な <a> タグに noopener だけでなく noreferrer も付けるべきか

追記

  • 2020/10/29: モダンブラウザによって noreferrer が自動的に付与されつつある、という説明をしていましたが、正しくは noopener の間違いでした。失礼しました。既に記事の方は修正済みです。

追記終わり。


  • target="_blank" は新しいタブでリンク先を開くためのオプションです
  • しかし、これにはリンク先のページからリンク元のページの window オブジェクトにアクセスできるという、ちょっと癖のある挙動があります
  • この挙動自体は脆弱性ではありませんが、 フィッシング詐欺などに悪用される可能性がある、危険な挙動であることが知られています
  • リンクのへの rel=noopener 付与による Tabnabbing 対策 | blog.jxck.io

noopener

  • そこで一般には、target="_blank"<a> タグには noopener を指定することが推奨されています
  • また、このオプションは 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 を参照されても問題ない、という発想
      • 同様の理由でメモリを覗き込まれても問題ないはず
    • リンク先から window.opener をどうしても参照したい時
      • window.opener 使いたいんだったら noopener 付けたら駄目でしょ、という当たり前の話
      • けどこれからの Web は Site Isolation をどんどん推し進めていく方向に発展すると思われるので、可能な限り window.opener を使わないほうが良いです
      • 代わりに MessageChannel という代替技術を使うのが推奨されています
  • とはいえいちいち same-origin かどうかを判定するのは面倒
    • なので基本的には target="_blank"<a> タグ全てに noopener を付けておく、という規約を導入するというのが個人的にはオススメです
      • 別に付けても何ら問題ではない / 付けておくだけ得 / 思考の手間が無くなる
      • うっかり noopener 付いていない <a> タグをコピペしてきて、 フィッシング詐欺に発展してしまう、みたいなことも防げる
    • あとは COOP を有効にするのも手

余談: 自動的な noopener の付与

noreferrer

  • 同じく target="_blank"<a> タグに指定できるオプションとして noreferrer というものがあり、これも noopener と合わせて使われることがあります
  • noreferrernoopener と一緒に使われるのは、古いブラウザで window.openerundefined にするため
    • というのも、window.opener が問題視され始めた当時は、noopener をサポートしていたブラウザには限りがあったため
    • 一方 noreferrernoopener よりも古くからあるオプションで、 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 の進化に置いてかれます。

ポケットモンスター・ポケモン・Pokémon・は任天堂・クリーチャーズ・ゲームフリークの登録商標です.

当ブログは @mizdra 個人により運営されており, 株式会社ポケモン及びその関連会社とは一切関係ありません.