べにやまぶろぐ

技術寄りの話を書くつもり

http のリクエストが勝手に https にリダイレクトされるときは Strict-Transport-Security を疑おう

mysite.jp というサイトを運用していて、新たに help.mysite.jp みたいな別サイトをサブドメイン切ってかつ別の Web サーバーで用意したとします。

このサイトはヘルプページがメインなので高額な SSL 証明書をとらず http で良しとしていた…つもりが http://help.mysite.jp へのリクエストが何故か勝手に https://help.mysite.jp にリダイレクトされてしまい挙げ句の果てに証明書のエラーが出てアクセスできない、なんてことがありました。

最初は help.mysite.jp に問題があると思って色々調べていたんですが、実は問題のあったのは mysite.jp の方。mysite.jp はログイン前提のサイトだったのでこんなヘッダを設定していました。

Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"

この Strict-Transport-Security、Strict-Transport-Security - HTTP | MDN によれば

HTTP Strict Transport Security (よく HSTS と略されます) は、Web サイトがブラウザに対して、HTTP の代わりに HTTPS を用いて通信を行うように伝達することができるセキュリティ機能です。

ということでどうせ https しか許可してないしということで設定していたのですが、このヘッダに includeSubDomains が付いていたために、後から作った help.mysite.jp にも同じポリシーが適用されてしまっていたことが https リダイレクトの原因でした。

ここでのポイントは mysite.jp と help.mysite.jp が違うWeb サーバーで運用されていて、かつ Strict-Transport-Security の設定は mysite.jp にしかされていなかったという点です。

何が起きているかというと、例えばあるユーザーが mysite.jp にアクセスしたとき(1)にサブドメインも対象にした Strict-Transport-Security 入りヘッダが付いたレスポンスを受けます(2)。

f:id:beniyama:20141108224904j:plain

このとき、ブラウザは内部に持っている HSTS 対象のドメインリストに mysite.jp を追加します(3)。ブラウザはこのように HSTS のポリシーを持っているドメインをキャッシュして、次につなぎにいく際は内部的にリクエストを https に書き換えます。これは、内部で処理を行うことで極力(脆弱な) http でつなぎにいくのを避ける目的があります。

今回のケースでは includeSubDomains でサブドメインも対象になっていましたので、次に HSTS ヘッダ無しで運用されている http://help.mysite.jp にアクセスしようとした際もブラウザ内のキャッシュ(HSTS リスト)が効いて https://help.mysite.jp に向いてしまうのでした(4)。

ユーザーが mysite.jp を訪れずに直接 help.mysite.jp にアクセスした場合は当然 HSTS 対象のドメインとして認識されていませんから問題なく http でサイトを表示することが可能です。

結局はブラウザ側でなされている脆弱性対応なのですが、最初に接続しにいって HSTS ポリシーを受け取るまではドメインが https を強制しているかわからず http でのやりとりになる可能性があります。また HSTS には max-age というオプションがありブラウザが HSTS ポリシーをキャッシュする期間を設定することができるのですが、あまり頻繁にキャッシュアウトするような短い期間を設定してしまうとその度に http でつなぎにいってしまう可能性がありますので、十分長い期間を設定することが推奨されているようです(max-age はサイトにアクセスするたびに更新され期限は延びます)。

今回の問題は、ブラウザが持つ HSTS リストに依るのでユーザー毎に挙動が違う、また外部サイト(この場合は mysite.jp )の設定に影響を受けていたという点でちょっと厄介だったかなと思います。

教訓としては、

Strict-Transport-Security を includeSubDomains 付きで設定するときは既存のサブドメイン全てのセキュリティポリシーを確認しましょう

というのと、

新たにサブドメイン切るときもそのルートドメインで includeSubDomains 付き Strict-Transport-Security を設定していないか確認しましょう

ということでしょうか。

ちなみに、Chrome などでは preloaded list として予めいくつかのドメインを HSTS リストに持っているようです。またユーザーが自分でドメインを HSTS リストに追加することもできますが、長くなってしまったのでそれはブラウザ間の挙動の違いと一緒にまた次回書こうと思います。

参考)