問題:実際に起きた怖い話
長いタイトルですいません。この問題はちょっとわかりづらいので、まず実例からお話しします。
僕の妻はクープバゲットというパン屋をやっていて、僕はそのWebサイト(メインサイト)を管理しています。
このWebサイトはRailsで構築しています。
このほかに、ブログサイトがあります。
ブログサイトの実体はTumblrですが、独自ドメインを充てています。
この時点で押さえておくポイントは以下の2点です。
- メインサイトはネイキッドドメインで、ブログサイトはサブドメインで運用している
- メインサイトはHTTPSで、ブログサイトはHTTPである
今朝、メインサイトのRailsのバージョンを5.0から5.1にアップグレードしました。
メインサイトは順調に動いていたのですが、Railsアップグレードとは無関係なブログサイトが急にアクセスできなくなりました。
以下がそのエラー画面です。
エラーメッセージをよく見ると、ブログサイトのURLが https://blog.coupe-baguette.com になっています。
しかし、ブログサイトはHTTPS対応していないので、当然HTTPSでは接続できません。
でもなんで勝手にHTTPSになってるの!?
・・・というのが、実際に起きた怖い話です。
問題の原因:HSTSがサブドメインに対しても有効になったため
ブログサイトが強制的にHTTPS接続になった原因は、メインサイトのレスポンスヘッダにありました。
以下がその問題の行です。
Strict-Transport-Security:max-age=15552000; includeSubDomains
この行はHSTS(HTTP Strict Transport Security)の設定情報です。
HSTSについては以下の記事で詳しく説明されています。
HSTS (HTTP Strict Transport Security) の導入 - Qiita
簡単にいうと、これはブラウザに対して、
「常にHTTPS接続してくれよ、サブドメインも含めてな!」
とお願いする設定です。
そのため、ブラウザはメインサイトのみならず、ブログサイトまでHTTPSで接続しようとしたのです。
ちなみにRails 5.0時代は以下のようになっていました。
Strict-Transport-Security:max-age=15552000
ご覧のとおりincludeSubDomains
が付いていないため、ブログサイトはHTTPS接続されることを免れていたのです。
Rails 5.1からincludeSubDomainsが付くようになった
includeSubDomains
オプションが付くようになったのは、このコミットの影響です。
実はincludeSubDomains
オプションは、新規にrails new
した場合に限り、5.0でも有効になっていました。
なぜなら、config/initializers/new_framework_defaults.rb
で明示的に設定されているからです。
# 省略
# Configure SSL options to enable HSTS with subdomains. Previous versions had false.
Rails.application.config.ssl_options = { hsts: { subdomains: true } }
しかし、Rails 4.xからアップグレードした場合はこのオプションが付きません。
そのため、新規に5.0でrails new
した場合と、4.xからアップグレードしたときで、設定内容が異なることになります。
上記のコミットはこの問題を避けるために、明示的にオプションを指定しなくても、そのバージョンのRailsのデフォルト設定を有効にするための変更です。
その結果、Rails 5.1からは新規にrails new
した場合と同様に、includeSubDomains
オプションが付くようになりました。
もうひとつの条件:force_sslがtrueのときだけHSTSの設定も有効になる
しかし、rails new
したRails 5.0や、アップグレードしたRails 5.1で必ずHSTSの情報が付与されるわけではありません。
HSTSの情報が付与されるのはforce_ssl
がtrue
になっているときだけのようです。
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true
解決策:HSTSのオプションを明示的に上書きする
解決策はデフォルト設定を明示的に上書きすることです。
今回は以前と同じ状態に戻すために、以下のような設定を入れました。
Rails.application.config.ssl_options = { hsts: { subdomains: false } }
これで以前と同じようにincludeSubDomains
オプションなしのHSTSになりました。
Strict-Transport-Security:max-age=15552000
なお、ssl_options
の設定値については以下のAPIドキュメントを参考にしてください。
この件のまとめ
というわけで、以下の条件が揃うときっと痛い目を見るよ!
- ネイキッドドメインのサイト(メインサイト)がHTTPS
- サブドメインがサイト(サブサイト)がHTTP
- メインサイトがRailsで構築されている
- メインサイトの
force_ssl
オプションがtrue
になっている - メインサイトをRails 5.1にアップグレードした
- メインサイトにアクセスしてからサブサイトにアクセスする → サブサイトまで強制的にHTTPS接続に
もしこの問題が発生したら、Railsのssl_options
を明示的に設定して、サブサイトがHTTPS接続されないようにしましょう。
全部の条件が揃うのはかなりレアかもしれませんが、みなさんも注意してくださいね。
Special thanks to
この件の調査と解決に協力してくれた同僚の @ruzia に感謝します。
あわせて読みたい
この問題とは直接関係ありませんが、妻のパン屋のWebサイトをデザインリニューアルしたときの話をこちらのブログに書いています。