知ってるようで知らないRefererとReferrer-Policyのお話

# 概要

この記事は主に以下の内容に触れていきます！

1. Refererというものはそもそもなんなのか
2. Refererはどのように動いているのか
3. セキュリティを意識したReferrer-Policyを設定するにはどうすればいいのか
4. 番外編: target=_blankでの脆弱性

つい半年ほど前にGoogleがChromeのデフォルトのReferrer-Policyを変更して話題になりましたね。
この記事を読んでいただければGoogleがReferrer-Policyを変更した背景も分かってくるかと思います！

参考記事： [Entry is not found - paiza開発日誌](https://paiza.hatenablog.com/entry/2020/10/02/referrer_policy)



# Referer is 何？

> HTTPリファラ（英: HTTP referer）あるいは単にリファラは、HTTPヘッダの1つで、インターネット上の1つのウェブページまたはリソースから見て、それにリンクしているウェブページやリソースのアドレス[1]を指す。リファラを参照することで、どこからそのページに要求が来たのかを知ることができる。

参照: [HTTPリファラ - Wikipedia](https://ja.wikipedia.org/wiki/HTTP%E3%83%AA%E3%83%95%E3%82%A1%E3%83%A9)
つまりRefererとはどのページからアクセスが来たか情報を渡すためのHTTPヘッダの種類の一つであると言えます！

例えばGoogle検索からの流入の場合HTTPのリクエストヘッダには`Referer: https://www.google.com/`の値がつき、この情報を集めることによってユーザーがどのサイトを経由してページにやってきたのか知ることができます。

Web広告を回すマーケターのかたにとっては必須の情報ですね！

HTTPについて詳しく知りたい方はこちらの記事に丁寧にまとめられています↓
[HTTPのバージョンについてまとめ](https://qiita.com/hirooka0527/items/13767855358f83db5e02)

*ちなみに`referer`の綴りが参照元という意味の英単語である`referrer`と違うのはHTTPが策定された時にヘッダ名を間違えたスペルで書いてしまい、それが今でも使われているという歴史的経緯があるようです...(ちなみに後からから出てくるReferrer-PolicyはスペルミスをしなかったそうでReferrerのままです笑)[Referrer-Policy - HTTP | MDN](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Referrer-Policy)



# Refererは誰が送ってるの?
Refererを送っているのはwebブラウザ(chrome, IE, FireFoxなど)です。
ブラウザが自動でRequestヘッダーのrefererに送信元のURL情報を付与しています。
ブラウザによってはReferer送信に対応していないものもあるのでこのサイトでチェックしてみて下さい！(ほとんどのモダンなブラウザでは対応しています)
[Can I use... Support tables for HTML5, CSS3, etc](https://caniuse.com/?search=referer)

# Refererが生じる脆弱性

Refererの情報は流入元を知れるためとても便利な反面、Refererによってもたらされる脆弱性もあります。
それはURLに重要情報が含まれていた場合、その情報が遷移先のサイトに送られてしまうことです。

### Refererによって重要情報が漏洩する例
例えば脆弱性の高いサイト`https://example.com`でユーザー登録をしたとしましょう。

登録ボタンを押すとメールが送信され、そのメールに以下のようなメールアドレスとパスワードが埋め込まれた`https://example.com/auth?email=hogehoge@gmail.com&password=hugahuga123&token=ndjask819Sjks`というリンクが貼られています。

URLに情報が既に入っていることにより、このリンクをクリックすることでメアドとパスワードを再度入力することなく、認証に成功します。

そしてサイト内の記事に貼ってある外部リンク`https://insecure.example.com`に遷移したとしましょう。そうするとreferer情報として`Referer: https://example.com/auth?email=hogehoge@gmail.com&password=hugahuga123&token=ndjask819Sjks`が`https://insecure.example.com`に送られてしまいます。

もしこのサイトの運営者が悪意のある人だった場合、Referer情報から個人情報を取得し、悪用することができてしまします。

どうすればこの脆弱性を回避することができるでしょうか？
URLにセキュアな情報を入れないようにすることは大事ですがUX向上のためどうしても入れたい場合(認証でのリダイレクトログインなど)もあるかと思います。

そんな時に登場するのが`Referrer-Policy`です。

# Referrer-Policyの役割

Referrer-Policyは以下のようなmetaタグに設定することでrefererを送信するブラウザの挙動を変更することができます。


例:

```:html
<meta name="referrer" content="strict-origin-when-cross-origin" />
```

この`content`の種類によってreferer送信の挙動をコントロールすることができます。

例えば
- `no-referer`: 全く送らない
- `strict-origin-when-cross-origin`: HTTPS間またはHTTP間のリクエストの時origin(サーバーのドメイン名またはIPアドレス)だけ送る、さらに同じoriginでのリクエストの場合は全てのURLを送る

などです！

設定できる値はこちらのサイトに詳しく載っています。
[Referrer-Policy - HTTP | MDN](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Referrer-Policy)

とりあえず一覧でざっくりとみたい！という方のために分かりやすい画像があったので添付しておきます。
![referrer-policies.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650287/9884e58c-179b-7a25-06e7-d2e4a54f53c5.jpeg)
画像URL: https://web.dev/referrer-best-practices/


このReferrer-Policyは実はブラウザごとにdefaultの値が決まっています。

各ブラウザの対応状況は以下の通りです
![スクリーンショット 2021-01-17 17.34.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/650287/9f788377-d06d-d86a-81d5-9730e6bc850b.png)

画像URL: [Referer and Referrer-Policy best practices](https://web.dev/referrer-best-practices/)
*Chromeは2020年8月26日よりdefaultが`strict-origin-when-cross-origin`に変更されました。


# Referrer-Policyのベストプラクティス

refererはユーザーの流入元が分かり便利な反面、個人情報などが外部のサイトに知られてしまう脆弱性があるというリスクがあることが分かりました。

それではReferrer-Policy設定のベストプラクティスは何なのでしょうか？

サイトにおける要件により変わってくるかと思いますが、大体のサイトで求められるのが

1. 外部サイトには全てのURLを送りたくない
2. 同じサイト内での遷移は全てのURLを送りたい(tracking等便利な為)
3. HTTPへリクエストを送るときはrefererを送りたくない(中間者攻撃を防ぐため)

の3点です。この条件を満たすのは`strict-origin-when-cross-origin`となるのでメタタグに

```:html
<meta name="referrer" content="strict-origin-when-cross-origin" />
```
を設定しておくことで適切かつ安全なrefererを送信できるようになります！

Chromeはこちらの設定がデフォルトになりましたがFireFoxやEdgeなどはデフォルトが`no-referrer-when-downgrade`になっているのでこちらのタグを明記しておくことが大切です！

最新の対応状況はこちらのサイトからご確認ください！
[Can I use... Support tables for HTML5, CSS3, etc](https://caniuse.com/referrer-policy)

# 番外編~別タブで外部リンクを開く場合(target=_blank)~

Googleのエンジニアが`<a>`タグで`target=_blank`を用いた別タブでの外部サイトを開く際に重大な脆弱性があることを指摘しました。
記事: [Links to cross-origin destinations are unsafe](https://web.dev/external-anchors-use-rel-noopener/)

記事によると
> target="blank" を使用して任意のページから別のページにリンクしている場合、リンク元のページとリンク先のページは同じプロセスで動作します。 そのため、リンク先のページで負荷の高い JavaScript が実行されていると、リンク元のページのパフォーマンスが低下するおそれがあります。
また、target="blank" にはセキュリティ上の脆弱性もあります。リンク先のページでは window.opener を使用して親ウィンドウのオブジェクトにアクセスしたり、window.opener.location = newURL によって親ページの URL を変更したりできます。

これはどういった脆弱性なのかというとtarget=_blankで開いたサイトではwindow.openerを使用することで親ページのURLを書き換え別のページに遷移させることができるといった脆弱性です。

こちらのサイトでとても分かりやすく(実際にリンクを踏んで挙動を確認することもできます)まとめられていたので、気になる方は確認してみて下さい。
[リンクを作る時の target="_blank" の危険性](https://yoru9zine.hatenablog.com/entry/2017/03/17/230729)

こちらの脆弱性を防ぐためには

```
<a href="https://examplepetstore.com" target="_blank" rel="noopener">
  Example Pet Store
</a>
```
のように`rel="noopenner"`を指定することで`window.opener`プロバティへのアクセスを防ぐことができます。
さらにrefererも送信したくない場合は`rel="noreferrer"`を設定することによって`window.opener`プロバティへのアクセス、refererの送信両方を防ぐことができます。

適切に使い分けて脆弱性とは無縁な安心安全のエンジニアライフを送っていきましょう！


# 参考サイト
- [Referrer を制御する](https://qiita.com/wakaba@github/items/707d72f97f2862cd8000)
- [セキュリティ対策のため、target="_blank"のa タグに "noopener noreferrer"のrel属性を自動で付与するjavascript文](https://qiita.com/TeruhisaFukumoto/items/eeabfc838eea1ed65023)
- [Links to cross-origin destinations are unsafe](https://web.dev/external-anchors-use-rel-noopener/)
- [Referer and Referrer-Policy best practices](https://web.dev/referrer-best-practices/)
- [Can I use... Support tables for HTML5, CSS3, etc](https://caniuse.com/referrer-policy)
- [リンクを作る時の target="_blank" の危険性](https://yoru9zine.hatenablog.com/entry/2017/03/17/230729)