Referrer-Policy によるリファラ制御
Intro
リファラはリンクなどでページを遷移する際に、遷移元の URL をリクエストの Referer ヘッダに載せる仕様である。
この付与はブラウザが自動で行うため、場合によっては非公開として扱っている URL が意図せず漏れることがある。
この挙動を制御することができる、Referrer-Policy ヘッダについて解説する。
Referer or Referrer
本来の英語としては RefeRRer が正しいが、HTTP Header ではスペルミスした RefeRer が互換性を保つためそのまま使われている。
しかし、新しく定義された Referrer-Policy は、正しいスペルが採用されている。
Referer ヘッダ
例えば https://example.com/index.html
に貼られたリンクから https://blog.jxck.io
に遷移する場合を考える。
そのリクエストには、条件次第で以下のように HTTP Referer ヘッダが含まれる。
GET / HTTP/1.1
Host: blog.jxck.io
Referer: https://example.com/index.html
blog.jxck.io
の管理者は、このリファラを見ることで、example.com
に自分のサイトへのリンクが貼られていることを知ることができる。
その性質上、個人情報の観点からこれを好まないユーザもおり、ブラウザには古くから Referer の送信を行わない設定が実装されていることが多い。
また、別途提供される拡張や、今回紹介する Referer Policy によっても、無効に設定することができる。
したがって、Application Logic の観点からは、Referer はあくまで補助的な情報として使われること(無くても動く)が望ましい。
用途によっては、別途定義されている Origin ヘッダを見る方が良い場合もある。
Referer の用途
簡易トラックバック
簡易的なトラックバックとして機能するため、アクセスログにこの情報を含むことで、流入元を解析するための情報源として使うのがメジャーなユースケースだろう。
例えば、検索エンジンの検索結果が検索キーワードを URL に含んでいれば、サイトオーナーはどういう検索結果からユーザが遷移してきたかを知ることができる。
Referer: https://search.example.com/q=http+referer+policy
Referer が送信された時だけ、そこから付加的な情報を得ることになる。
CSRF 対策
CSRF 対策の 1 つとして Referer を確認するという方法がある。
しかし、この場合も前述の通り、あくまでも補助的な効果しか得られない場合もある。
また、Form からの POST を対象とする場合は Origin ヘッダの方が用途として適しているだろう。
ただし、Origin ヘッダにしても Referer ヘッダにしても、値が予測可能なため、任意のヘッダを含むことが可能な脆弱性が Extension や Plugin で見つかるとバイパスが可能となる。
そうした脆弱性は、残念ながら 最近でも見つかっている ため、いまだに Token ベースの制御が行われている。
(SameSite Cookie などが普及すればこの部分はもっと変わって行くだろうと思われるが、今回は触れない)
一方で、どんなに Token が正しくても Referer が明らかにおかしければ攻撃の可能性もあるため、やはり補助的な情報として採用は可能だ。
盗用防止
画像などのサブリソースが直リンクによって無断転載されることを防ぐために、Referer が他のサイトからのものであればブロックするといった使われ方が存在する。
確実に盗用を防ぐことはできず、あくまで補助的な効果となる。
逆に、Referer を必須にしてしまうと、Referer を送らないユーザには、意図したページ上でも画像が表示されないことになるため注意が必要だ。
Origin ヘッダはサブリソースへの GET には 付与されない ため、代替はできない。
Referer による情報漏洩
ブラウザが Referer を自動で付与する挙動を意識していないと、意図せず非公開として扱っていた URL が漏れる場合がある。
URL が知られることを望まない例としては大きく以下があるだろう。
- URL を知っている人だけアクセス可能な限定共有 URL
- イントラ(企業内など)に閉じた非公開 URL
限定共有 URL の漏洩
身近な例として Private Gist のように、URL さえ共有すればアクセスできるタイプのサービスがよくある。
こうしたサービスは、検索エンジンのクローラなどに対しては robots.txt などで index を拒否している場合が多い。
しかし、Referer を制御しないと、リンク先に URL が送られ、意図しない人からアクセスされる可能性がある。
イントラ情報の漏洩
例として、架空の Example 社が Orange という新プロジェクトを開始した場合を考える。
Orange については、商標の関係もありプレスリリースまでは名前含め非公開だ。
Example 社は社内で Orange プロジェクトに関する情報共有のために、社内ブログやチケット管理などを運用している。
ここで、社内ブログやチケットに誰かのブログへのリンクが貼ってあった場合、そのリンクを踏んだブラウザからのリクエストに、このドメイン名が入った Referer が付与される。
https://orange.trac.example.com/
ドメインだけで無く URL エンコードされている日本語が入っている場合もある。
https://ticket.orange.example.com/%7B%E7%A4%BE%E5%93%A1%E5%90%8D%7D/issue/xxxxxx/[%E7%A4%BE%E5%A4%96%E7%A7%98]1%E6%9C%8811%E6%97%A5%E3%81%AE%E3%83%97%E3%83%AC%E3%82%B9%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%B9%E5%8E%9F%E7%A8%BF
// https://ticket.orange.example.com/{社員名}/issue/xxxxxx/[社外秘]1月11日のプレスリリース原稿
他にも、キーワード検索の結果から外部のページに飛べる場合、検索したキーワードが残る場合もある。
https://intra.orange.example.com/search?q=Apache+CVExxxxx
リンクされたブログの持ち主は、たとえそのリンクを辿ってアクセスができないとしても、Example 社が Orange というプロジェクトをやっていることや、付随する情報が推測できる可能性がある。
よく設計された URL は情報量が多く、多弁だ。
Referer の制御
こうした理由から、コンテンツに応じて、何らかの形でリファラを制御するといったことは、古くから行われている。
Firefox の about:config や、Chrome のコマンドラインオプションなど、リファラの送信を制限するオプションを提供するブラウザもある。
また、それらを実現する拡張も存在する。
これは、組織からの情報漏洩以前に、個人情報の観点から、ユーザの選択肢として古くから実装されていると筆者は認識している。
社内ネットワークに Proxy を立てている場合は、その Proxy で Referer ヘッダを落とす/書き換えるといった運用方法もある。
この場合、Referer に社内のドメインがある場合だけ落とすといった実装が可能なため、社外にあるサービスに対しての不具合は少ないだろう。
しかし、HTTPS 化が進んでいる昨今、実質的に MITM を行なっているこの方法は限界と言える。
証明書の運用次第では暗号化を解くこともできなくないかもしれないが、運用は難しだろう。
そもそもブラウザが自動的に Referer を送るという挙動が、コンテンツに合わせて制御できれば、こうした問題は解決する。
そこで、提案されたのが Referrer Policy である。
社内で HTTP サーバを立てる際に、このヘッダを適切に設定することで、意図しない漏洩のリスクを軽減できるだろう。
また、Origin を軸としたいくつかの設定が可能なため、単純に Referrer を一切無効にすることによる不具合などを防ぐこともできる。
ブラウザ設定
社内 Proxy
Referrer Policy
Referrer-Policy
Referrer-Policy は、ヘッダもしくは HTML の特定の要素に指定し、そのページからの遷移に伴う Referer ヘッダを制御するための Policy である。
執筆時点で、仕様には以下の Policy が定義されている。
no-referrer
no-referrer-when-downgrade
same-origin
origin
strict-origin
origin-when-cross-origin
strict-origin-when-cross-origin
unsafe-url
""
ポリシーの違いを見極める観点としてはまず載せる値がある。
ヘッダに URL が載る場合、例えばこのページでは以下のようになる。
Referer: https://blog.jxck.io/entries/2018-10-08/referrer-policy.html
ポリシーによっては、URL 全体ではなく Origin のみを送るものがある。
Referer: https://blog.jxck.io/
どのサイトから来たかはわかるが、それ以上の情報は載らないという状態になる。
これは Origin ヘッダが付与する情報と同等であり、Origin ヘッダが送信される状況では Referer を禁止しても同等の情報が送信されている ことは認識しておきたい。
以上を踏まえて、それぞれ解説していく。
Referer ヘッダそのものが省かれるため、情報の漏洩の観点から言うと一番安全ではある。
しかし、同じサイト内でも送られなくなるため、Referer をベースとした検証がどこかに入っている場合は問題になる。
社内ドメイン内の遷移でも送られなくなるので、内部での遷移についてもメトリクスを取りたい場合を考えると制限が強い。
また、Referer ヘッダは消えるが Origin ヘッダは残る点は意識しておきたい。
Referer ヘッダに URL 全体を載せ、必ず送る。
これを unsafe と言う理由は、http -> http や https -> http(Downgrade) でも送られるところにある。
平文通信の場合は、先ほどの Proxy のように MITM で削除したり、改ざんすることが可能であるためだ。
unsafe-url と同じ条件だが、送る値が Origin のみになる。
Path 以下の情報が送られない点で、漏洩の観点からは情報が絞られているが、平文通信でも送られる点はそのままだ。
Origin が一致した場合だけ URL 全体を送る。
つまり Downgrade では送られないが、Upgrade でも送られない。
さらに、http -> http では送られるということになる。
条件は strict-origin と同じだが、URL 全体を送る。
これがブラウザのデフォルトの挙動とされている。
内部遷移では URL 全体を送るが、外に出る場合は Origin のみを送る挙動になる。
Down/Upgrade も Cross Origin 扱いになるため、Origin のみ送られる。
改ざんを避けつつ、内部では URL を送り、外部には Origin だけに制限する。
多くの場合は、この設定が適用できるのが理想だろう。
なお、前述した Private Gist はこれを適用している。
Policy を指定しないことを意味する。
より上位の指定内容か、User Agent のデフォルトが反映される。
表にまとめるとこうなる
no-referrer
unsafe-url
origin
same-origin
strict-origin
origin
と同じく Origin の情報のみを送るが、Downgrade の場合だけ送らない。
no-referrer-when-downgrade (default)
origin-when-cross-origin
strict-origin-when-cross-origin
orign-when-cross-origin
に Downgrade での送信禁止を追加した挙動となる。
空文字列
Policy のまとめ
Policy
Condition
Value
noreferrer
必ず
無し
unsafe-url
必ず
URL
origin
必ず
Origin
same-origin
Same Origin
URL
strict-origin
Downgrade 以外
Origin
no-referrer-when-downgrade
Downgrade 以外
URL
origin-when-cross-origin
Cross Origin
Origin
origin-when-cross-origin
Same Origin
URL
strict-origin-when-cross-origin
Downgrade
無し
strict-origin-when-cross-origin
Cross Origin
Origin
strict-origin-when-cross-origin
Same Origin
URL
Referrer-Policy の適用方法
Policy を適用する方法は 4 つある。
優先度順に並べると以下だ。
rel=noreferrer
- referrerpolicy 属性
<meta>
の referrer 属性- HTTP の Referrer-Policy ヘッダ
仕様: https://html.spec.whatwg.org/multipage/urls-and-fetching.html#referrer-policy-attribute
値が違うのは Referrer-Policy が策定される前の仕様だからであり、ここに適用できる値は なお、この時 つまり
は
と同じ挙動となる。
値は前述した policy から選んで適用できる。
後述するページ全体への設定より優先されるため、特定のリンクだけ挙動を変えると言った使い方ができる。
つまり、markdown から html に変換する CMS などを使っている場合は、ビルド時に外部へのリンクだけ policy をつけると言った使い方ができる。
仕様: https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-delivery-referrer-attribute
サーバ指定だと影響範囲が大きい場合に CMS のテンプレートに追加するといった適用が考えられる。
なお、 仕様: https://html.spec.whatwg.org/multipage/semantics.html#meta-referrer
レスポンスの HTTP ヘッダで指定することにより、ページ全体に Policy を適用できる。
コンテンツに手を入れられない場合や、漏れなく全てのレスポンスに適用したい場合などに利用できる。
仕様: https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-header-dfn
<a>
, <area>
の rel=noreferrer
<a>
と <area>
には rel=noreferrer
をつけることで、その遷移のリクエストを no-referrer
の挙動とすることができる。
<a href="https://example.com" rel="noreferrer">
noreferrer
のみだ。
target="_blank"
を使用すると別タブが開き、これは Opener を生成しない。
<a href="https://example.com" rel="noreferrer" target="_blank">
<a href="https://example.com" rel="noreferrer noopener" target="_blank">
referrerpolicy 属性
<a>
などに対して referrerpolicy 属性を付与することができる。
<a href="http://example.com" referrerpolicy="origin">
<a>
には必ずこの属性をつけるが、Policy 自体はページ全体のものに準拠したいといった場合は、属性を ""
(空文字) にすれば良い。
<meta>
の referrer 属性<meta>
タグを利用することでページ全体に Policy を適用できる。
<meta name="referrer" content="origin-when-cross-origin">
never
/ default
/ always
といった古い仕様が存在したため、対応表を元に新しい値を指定することが望ましい。
HTTP Header
Referrer-Policy: no-referrer
DEMO
動作するデモを以下に用意した。
本サイトへの適用
本サイトには漏れて困る URL はない。
また、技術ブログという性質上、外部のリソースを参考としてリンクすることはよくあり、それらリンク先のサイトに対して、どのページからリンクされているのかを隠すつもりは無い。
Cross Origin で URL 全体を送るのは unsafe-url
か no-referrer-when-downgrade
であり、その差は Downgrade の扱いになる。
本サイトは HSTS 対応済みであるため、no-referrer-when-downgrade
では、HTTP しか提供していないサイトに対しては Referrer が飛ばないことになる。
しかし、近年の動向からも、リンクする先が HTTPS に対応している方が本サイトとしても望ましいため、no-referrer-when-downgrade
を採用することにした。
この Policy はブラウザのデフォルトとされているため、明示的な Referrer-Policy Header の追加は行わないこととする。