target=”_blank”を使うときはrel=”noreferrer”を指定しよう

target=_blankを使うときはrel=noreferrerを指定しよう

ウェブサイトを運営する際、外部リンクを新しいタブやウィンドウで開くために、リンクにtarget="_blank"を指定することがあります。しかし、この指定だけではセキュリティ上のリスクが生じる可能性があります。そこで、rel属性にnoopenernoreferrerを追加することが推奨されています。この記事では、noopenernoreferrerの違い、サポートブラウザの違い、両方を併用する理由、そしてそれぞれの使い分けについて解説します。

目次

noopenerとは?

<a href="..." target="_blank" rel="noopener">リンク</a>

noopenerは、リンク先のページが元のページ(リンク元)への参照を持たないようにするための値です。具体的には、target="_blank"を指定したリンクをクリックすると、新しいタブやウィンドウでリンク先が開かれますが、その際、リンク先のページからwindow.openerプロパティを介してリンク元のページにアクセスできてしまいます。これにより、リンク先のページがリンク元のページを操作するリスクが生じます。rel="noopener"を指定することで、window.openerプロパティがnullとなり、リンク先からリンク元へのアクセスを防ぐことができます。

noreferrerとは?

<a href="..." target="_blank" rel="noreferrer">リンク</a>

noreferrerは、noopenerの機能に加えて、リンク元のURL情報(リファラー)をリンク先に送信しないようにするための値です。通常、リンクをクリックすると、リンク元のURLがリファラーとしてリンク先に送信されますが、rel="noreferrer"を指定することで、これを防ぐことができます。これにより、プライバシーの保護や、リンク元の情報漏洩を防ぐことが可能となります。

サポートブラウザの違い

noopenernoreferrerのサポート状況はブラウザによって異なります。主要なブラウザの対応状況は以下の通りです。

noopenerをサポートするブラウザ

noopenerは比較的新しい仕様で、対応しているブラウザは後述のnoreferrerと比べると狭くなります。

rel=noopener – HTML: ハイパーテキストマークアップ言語 | MDN

noreferrerをサポートするブラウザ

前述のnoopenerと比べると対応しているブラウザの範囲は広くなります。

rel=noreferrer – HTML: ハイパーテキストマークアップ言語 | MDN

両方を同時に指定することが推奨されていたのは何故か?

<a href="..." target="_blank" rel="noopener noreferrer">リンク</a>

ここまで見ると、noreferrerはカバーしている機能もサポートブラウザもnoopenerよりも広いため同時に指定する理由はないように見えます。

では何故同時に推奨されていた時期があったのでしょうか?

この点について理解するためにnoopenernoreferrerに関する議論をいくつか調べました。

同時に指定することを推奨されていた理由

過去の話であるため、どこまで信憑性があるかは不明ですが、次のような理由でnoopenernoreferrerを両方指定することが推奨されていた時期があったようです。

  • 仕様に準拠していないレガシーブラウザに対応するため
  • Firefox 33-35で発生していた不具合に対する対策

非準拠ブラウザに対する対策

仕様に準拠していないブラウザでnoreferrerを指定してもwindow.openernullにならないことに対する対策として、noopenerを同時に指定していた、そういうブラウザが存在する可能性を想定して同時に指定していたようです。

@seancrater I don’t; the spec is often aspirational, and doesn’t describe historical (noncompliant) engines.

In general, if any version of any browser ever required both, then we should use both for the foreseeable future.

確かに、過去ChromeとFirefoxで動作が異なったり、一部のブラウザで問題が起きるといったことはありました。以下の記事では、EdgeでYahoo! Japanを表示したときに正しく表示されないという問題について問い合わせています。

今後もこのように特定のブラウザの特定のバージョンでのみ不具合が起きる可能性を考慮し、同時に指定することに対して、若干バンドルサイズが大きくなることしかデメリットがないことを考えると両方を指定しておくことは心理的安心という面からも適切な判断だと考えられます。

Firefox 33-35で発生してた不具合に対する対策

もう一つの理由はFirefox 33-35で発生していた、noreferrerを指定すると別タブで開けなくなる不具合に対する対策で、ESLintのIssueでこの点について議論されています。

Actually, I found a counter-example: Firefox 33–35 removes opener with rel="noreferrer" but doesn’t with rel="noreferrer noopener" (probably because it encounters an unsupported rel value). If you have a Browserstack account, you can verify it by opening http://noreferrer.hypnosphi.de (source) in FF 33–35 and clicking the links there.

Firefox側のIssueを見ると、noreferrerをつけていてもリファラーを送っているとありますが、これがwindow.openernullになっていないことと同じことなのかは今ひとつわかりませんでした。

If you open a link with the rel=”noreferrer” attribute in a new tab (eg. over middle click) it will send a referrer anyway.

ESLintではnoopenernoreferrerを両方指定するようにチェックしていた時期がありましたが、現在ではnoreferrerがついていることをチェックし、noopenerもついているかどうかはチェックしないようになっています。

WordPressでrel=”noopener”が自動付与される理由

WordPressでは、バージョンによって、target="_blank"を持つリンクにrel="noopener"が自動付与されるのか、rel="noopener noreferrer"が付与されるのかが変わっていたようです。

WorkPress 4.7.4からセキュリティ対策のためにrel="noopener noreferrer"が自動的に付与されるようになりました。しかし、WordPress 4.8では、rel="noopener"だけが自動付与されるように変更されています。

WordPress 5.0では新しいエディタ「Gutenberg」が導入され、rel="noopener noreferrer"が自動付与されるようになった一方で、クラシックエディタを使用している場合にはrel="noopener"だけが自動付与されるようになっていました。

WordPress 5.6ではrel="noopener"だけが自動付与されるようになり、以降はrel="noopener"だけが自動付与される仕様に落ち着いているようです。

WordPressが、rel="noreferrer"ではなくrel="noopener"を付与しているのは、セキュリティ対策をしつつアフィリエイトリンクなどのためにリファラーを送りたい(参照元を知らせたい)という点にあります。認証が必要なページと違って、ページのURLを知ってもらう機会を少しでも減らさないためにも、noreferrerではなくnoopenerが求められます。

パフォーマンス対策としてのnoopener

仕様には記載がなく、実装依存の話としてnoopenerを指定する効果のひとつにパフォーマンス対策を挙げている記事を見かけます。しかし、この主張は必ずしも正しいというわけではなく、ブラウザの実装依存であるという点は理解しておく必要があります。

window.openernullでない場合、元ページを開いているタブとtarget="_blank"で開いたページのタブは同じプロセス・スレッドで動作することになり、開いたページで重いスクリプトを実行すると、元ページのパフォーマンスにも影響を与えるというものです。

実際のブラウザのコードを見たわけではありませんが、最近のバージョンではここら辺は対策済みになっているのではないかと思います。タブの数だけスレッドが作成されるわけでもありませんし、特のタブが応答しなくても他のタブは問題なく操作できているので、主要ブラウザではこの点はあまり気にする必要がないと思います。

結局、どれを付与すればよいのか?

noopenernoreferrerのどちらを付与すればよいかについてまとめます。

「どちらも付与しない」は非推奨

タブブラウザが登場する以前ならまだしも、現在のブラウザではtarget="_blank"を指定した場合はnoopenernoreferrerのいずれかを指定すべきです。特に外部サイトのページをtarget="_blank"で開く場合はrel属性を付与することは必須であるといっても差し支えないと思います。

同一サイト内であっても、モードレスダイアログのようなユーザー体験を実現したければ、別タブで操作させるのではなく、同一タブ内にモードレスダイアログをHTML/CSS/JSを使って実現すれば済むので、そういった意味でもwindow.openerを使うことはもうないのではないかと思います。

認証が必要なページから外部ページを開くときはnoreferrerを指定する

認証されたユーザーのみが表示できるページで外部ページを開くときはそのページのURLを知られないようにするという意味でもnoreferrerを指定することが強く推奨されます。

もう少し簡単に言ってしまえば、リファラーを送りたいケース以外はnoreferrerを指定するのがよいと思います。noreferrerの方がサポートされているブラウザの範囲が広いため、セキュリティ対策を考えるとnoreferrerを使う方が安全なためです。

リファラーを送信する必要がある場合のみnoopenerを指定する

ブログやアフィリエイトサイトなど、target="_blank"で表示したサイトに元ページのURLを伝えたい、流入元がどこかを伝えたいというケースにおいてはnoopenerを指定するのがよいでしょう。

WordPressがnoreferrerを自動付与せずnoopenerを自動付与しているのはこれが理由だと考えられます。アフィリエイトの種類にもよりますが、リファラーがないとアフィリエイト報酬に計算されない場合もnoreferrerではなくnoopenerを使う必要があるので、アフィリエイトサイトの仕様を事前に確認しておくことが重要です。

rel属性が設定されていることをチェックしよう

基本的にはESLintなどの静的解析ツールでチェックするのが望ましいですが、アプリケーション開発ではURL部分が変数になることがあり、この場合は静的解析ツールではチェックしきれないという問題があります。

そのため、静的解析だけを頼りにするのではなく、他の手段も組み合わせて多層的にチェックするのがよいでしょう。

静的解析ツールでチェックする

チェック漏れを防ぐという点ではESLintなどの静的解析ツールを導入し、エディタの拡張機能でチェックしたり、CIでチェックすることは非常に重要です。しかし、すべてのケースをチェックしきれなかったり、必要ない場合でもエラーになるなどの弊害もあるため、導入できるかどうかはチームで慎重に検討することが望ましいです。

ESLintでチェックする場合、

npm install --save-dev eslint @html-eslint/parser @html-eslint/eslint-plugin

でHTMLファイルをチェックするためのプラグインも導入します。

設定ファイルでは、

import html from "@html-eslint/eslint-plugin";
import htmlParser from "@html-eslint/parser";
...

export default defineConfig([
  ...
  {
    files: ["**/*.html"],
    plugins: {
      "@html-eslint": html,
    },
    languageOptions: { parser: htmlParser },
    rules: {
      "@html-eslint/no-target-blank": "error",
    },
  },
]);

HTMLファイルを処理するルールを追加することで、HTMLファイルに対してLintingできるようになります。

aタグにtarget="_blank"があり、href属性が相対パスでない場合に、rel="noreferrer"がないと

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <a target="_blank" href="http://example.com/">Home</a>
  </body>
</html>

以下のようにエラーが出るようになります。

> test1@1.0.0 lint
> eslint *.html


/Users/user/test/index.html
  9:8  error  Missing `rel="noreferrer"` attribute in a tag  @html-eslint/no-target-blank

相対パスの場合はチェックが行われない点に注意が必要です。(あくまでも外部サイトを表示するためのセキュリティ対策にフォーカスされている)

また、先ほど参照したESLintのIssueでも触れられていますが、noopenerの有無はチェックしないようになっているので、rel="noreferrer"rel="noopener noreferrer"(順不同)のいずれかの場合にパスするようになっているため、noopenerがあることをチェックできない点にも注意してください。

CopilotなどのAIエージェントにチェックしてもらう

Copilotなどソースコードを参照できるAIエージェントにチェックしてもらうというのも手です。ワークスペース全体をチェックできる場合は網羅的にチェックしてもらえるので、非常に効率的です。

レビューの観点に加える

修正箇所に限定するのであれば、プルリクなどのレビューの観点としてリンク使用時にrel属性を付与しているかをチェックするのもよいでしょう。ただし、チェック漏れのリスクもあるため、前述のようなツールでのチェックを併用したり、チェックリストでのチェックを行うなどの対策を併用することも検討すべきです。

まとめ

noopenernoreferrerを指定することでどういった効果があるのか、これらのrel属性に対して過去にどのような経緯があったのかについて確認しました。

また、現在においてはどのように指定すればいいのか、どうやってrel属性の付与漏れを防ぐのがよいかについてもまとめました。

動作確認という点では、rel属性の有無にかかわらず問題なく動いてしまうので、発見しにくい問題ではありますが、セキュリティに関する重要な対策の1つであるため、ツールなども活用しながら付与漏れがないようにしていくことが重要です。

昨今ではCopilotなどでワークスペース全体をチェックすることも可能になってきているので、今一度点検をしてみてもよいかもしれません。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次