先日、はてなブログが HTTPS に対応したということで、このブログも HTTPS 化を実施しました。 このエントリを見ているとき、ブラウザのロケーションバーを見ると HTTPS になっていることが確認できると思います。
今日は、この HTTPS 化にからめて、混在コンテンツ、HTTP Strict Transport Security (HSTS) 、upgrade-insecure-requests あたりをまとめてみたいと思います。
HTTPS 化の流れ
そもそも、みなさんご存知のように、最近は全サイトを HTTPS 化しようという流れになっています。
下記のページに依ると、2018/04/14 における「Chrome で HTTPS 経由で読み込まれたページの割合」は、61 % になってたりします。
HTTPS 化への流れはセキュリティが主題ですが、これに伴い、SEO にも関連します。
混在コンテンツ
で、HTTPS 化するにあたって、必ず出てくるのがこの混在コンテンツ(mixed contents) の問題です。
混在コンテンツというのをざっくり言うと、HTTPS で提供されるページから HTTP 経由で読みこまれるリソースです。 こうなると、(Secure じゃない)Cookie の盗聴やら、中間者攻撃やらにさらされるので、基本的には忌むべき存在となります。
この混在コンテンツには、大きく以下の 2 種類に分けられます。
- Optionally-blockable Content
- Blockable Content
このあたりは、Mixed Content に定義があります。
Optionally-blockable Content は、HTTPS ページから読み込まれる <img>
、<video>
、<audio>
といったリソースで、
各ブラウザは警告は出すものの、リソースを 読み込む という判断をしています。
セキュリティのリスクはあるはあるんですが、そのリスクよりも、「読み込めないと判断したときの影響」の方がでかいよね、って話です。
一方、Blockable Content はそれ以外のリソースで、代表的なのは <script>
とかです。
こっちについては、ブラウザはセキュリティリスク高と判断して読み込みをブロックします。
頻繁に見ると思うんですが、以下が Chrome のコンソールに出力されるログですね。上が Optionally-blockable、下が Blockable。
HSTS
で出てきたのが HTTP Strict Transport Security (HSTS) です。
HSTS は簡単に言うと、サイトを閲覧するときに、HTTP ではなくぜったいに HTTPS にしようぜっていう仕組みです。 だいたいブラウザとサーバ間の挙動は以下のようになります。
ブラウザが HTTP のページにアクセスした場合
- ブラウザが HTTP のページにアクセスする
- サーバはステータスコード 301 でリダイレクト
→以下、次のフローに乗る
ブラウザが HTTPS のページにアクセスした場合
- ブラウザが HTTPS のページにアクセスする
- サーバは
Strict-Transport-Security
(STS)ヘッダを返す- twitter.com は
strict-transport-security: max-age: 631138519
- wikipedia.org は
strict-transport-security: max-age=106384710; includeSubDomains; preload
- twitter.com は
- ブラウザは、対象ホストが:
- まだブラウザ内に HSTS Host として登録されていない場合は、対象ホストを HSTS Host として登録する
- ブラウザ内に HSTS Host として登録されている場合は、STS ヘッダの内容をブラウザ内に登録する
ミソは 3. で、この後、ブラウザが対象ホストにアクセスする場合は http ではなく意地でも https にアクセスします。
この辺りの挙動は Chrome だと分かりやすくて、以下が http://www.wikipedia.org
にアクセスしたときのヘッダなのですが、
307 Internal Redirect
で https にリダイレクトするようになっています。このリダイレクト、「Internal Redirect」の Phrase のとおり、Chrome 内部での疑似レスポンスです。
Firefox の場合はこの疑似リダイレクトとかは見えません。ロケーションバーに http://www.wikipedia.org
と打ち込んでエンターを押しても、勝手に https でアクセスしたことになります。
あれ、少なくとも一度 HTTP にアクセスするならそこを MITM で狙われるのでは
これはそのとおりで、ブラウザが HSTS ホストとして認識していないホストにアクセスするときは、HTTP でアクセスせざるを得ないことが有り得ます。
この対策として、各ブラウザはそのリリース段階で多数のホストを HSTS ホストとして登録しています。上に挙げた twitter.com
とか wikipedia.org
とかはまさにその例です。
こうやって、一度たりとも HTTP を許さない方法を Preloaded HSTS とか呼んだりします。
この Preloaded HSTS の対象ホストの一覧は、
- Chrome: Blame - net/http/transport_security_state_static.json - chromium/src - Git at Google
- Firefox: mozilla-central: security/manager/ssl/nsSTSPreloadList.inc@90b96acae18ae3424142964cd4a8df5ef26a8560 (annotated)
にあります。Chrome のリストに追加希望がある場合は、たしか HSTS Preload List Submission から申請できるはず。
HSTS ホストとして登録されているかを確認する方法
Preloaded HSTS ホストなら上記の一覧を見れば良いですが、Preloaded でないホストを含めて自分のブラウザを確認したい場合は、chrome://net-internals/#hsts
にアクセスすれば良いです。
こんな感じでクエリできます。
upgrade-insecure-requests
上述の通り、完全 HTTPS 化できたサイトは HSTS 適用すれば良いんですが、一部 HTTP が残っちゃう、みたいな場合に役立つのが、この upgrade-insecure-requests です。 Content-Security-Policy (CSP) の一種なんですが、upgrade-insecure-requests の目的は、「HTTPS にマイグレーションするときの管理者/開発者の負荷を下げること」にあります。
挙動については、HTTP のレスポンスヘッダに Content-Security-Policy: upgrade-insecure-requests
が含まれているページで、以下のような http 経由の画像参照があった場合、
<img src="http://example.com/image.png"> <img src="http://not-example.com/image.png">
ブラウザはあたかも以下のように書いてあるものと解釈して、画像のフェッチを実行します。
<img src="https://example.com/image.png"> <img src="https://not-example.com/image.png">
もちろん、これらの画像が HTTPS で取得できない場合は、http に fallback することなくネットワークエラーとして扱われます。
HSTS と競合するのでは?
上記のように,強制的に http -> https と変更するという意味では、HSTS と同じようにも思えるのですが、W3C 的にも upgrade-insecure-requests だと HTTP 通信が行われることを防げないことを記載しています。
Authors can and should continue to use that header to ensure that their users are not subject to SSL stripping downgrade attacks, as the upgrade-insecure-requests directive will not ensure that users visiting your site via links on third-party sites will be upgraded to HTTPS for the top-level navigation.