Chapter 30

SSLを利用する

koga1020
koga1020
2021.11.23に更新

SSLを利用する

アプリケーションがSSLでリクエストに応答するように準備するには、少しの設定と2つの環境変数を追加する必要があります。SSLが実際に動作するためには、認証局の鍵ファイルと証明書ファイルが必要です。必要な環境変数はこれら2つのファイルへのパスです。

設定はエンドポイント用の新しい https: キーで構成され、その値はポート、キーファイルへのパス、証明書(PEM)ファイルへのパスのキーワードリストです。アプリケーションの名前を表す otp_app: キーを追加すると、プラグはアプリケーションのルートでそれらのファイルを探し始めます。これらのファイルを priv ディレクトリに置き、パスを priv/our_keyfile.keypriv/our_cert.crt に設定します。

以下は config/prod.exs の設定例です。

import Config

config :hello, HelloWeb.Endpoint,
  http: [port: {:system, "PORT"}],
  url: [host: "example.com"],
  cache_static_manifest: "priv/static/cache_manifest.json",
  https: [
    port: 443,
    cipher_suite: :strong,
    otp_app: :hello,
    keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
    certfile: System.get_env("SOME_APP_SSL_CERT_PATH"),
    # OPTIONAL Key for intermediate certificates:
    cacertfile: System.get_env("INTERMEDIATE_CERTFILE_PATH")
  ]

otp_app: キーがない場合、プラグがファイルを見つけるためには、ファイルシステム上のどこにあってもファイルへの絶対パスを指定する必要があります。

Path.expand("../../../some/path/to/ssl/key.pem", __DIR__)

https: キーの下にあるオプションはプラグアダプター(通常は Plug.Cowboy)に渡され、プラグアダプターは Plug.SSL を使用してTLSソケットのオプションを選択します。利用可能なオプションとそのデフォルト値の詳細については、Plug.SSL.configure/1のドキュメントを参照してください。Plug HTTPS GuideErlang/OTP sslのドキュメントも貴重な情報を提供しています。

開発環境でのSSL

開発でHTTPSを使いたい場合、mix phx.gen.cert を実行することで自己署名証明書を生成できます。これにはErlang/OTP 20以降が必要です。

自己署名証明書があれば、config/dev.exs の設定をHTTPSエンドポイントを実行するように更新できます。

config :my_app, MyAppWeb.Endpoint,
  ...
  https: [
    port: 4001,
    cipher_suite: :strong,
    keyfile: "priv/cert/selfsigned_key.pem",
    certfile: "priv/cert/selfsigned.pem"
  ]

これは http の設定を置き換えることもできますし、異なるポートでHTTPとHTTPSサーバーを実行することもできます。

強制SSL化

多くの場合、HTTPをHTTPSにリダイレクトすることで、すべての受信リクエストにSSLを使用させたいと思うでしょう。これはエンドポイントの設定で :force_ssl オプションを設定することで実現できます。これは Plug.SSL に転送されるオプションのリストを渡す必要があります。デフォルトでは、HTTPSリクエストに "strict-transport-security" ヘッダーが設定され、ブラウザは常にHTTPSを使用するように強制されます。安全でない(HTTP)リクエストが送信された場合、:url 設定で指定した :host を使ってHTTPSバージョンにリダイレクトします。たとえば、次のようになります。

config :my_app, MyApp.Endpoint,
  force_ssl: [rewrite_on: [:x_forwarded_proto]]

現在のリクエストの host に動的にリダイレクトするには、:force_ssl の設定で :hostnil に設定してください。

config :my_app, MyApp.Endpoint,
  force_ssl: [rewrite_on: [:x_forwarded_proto], host: nil]

これらの例では、rewrite_on: キーは、リバースプロキシやロードバランサーがアプリケーションの前で使用するHTTPヘッダーを指定し、リクエストがHTTPで受信したかHTTPSで受信したかを示します。TLSを外部要素にオフロードすることの意味合い、とくにセキュアクッキーに関連する詳細については、Plug HTTPSガイドを参照してください。このドキュメントで Plug.SSL に渡されるオプションは、Phoenixアプリケーションの force_ssl: エンドポイントオプションを使って設定する必要があることに注意してください。

HSTS

HSTSまたは "strict-transport-security" は、ウェブサイトが安全な接続(HTTPS)を介してのみアクセス可能であることを宣言できるようにする仕組みです。SSL/TLSを剥奪する中間者攻撃を防ぐために導入されました。これにより、WebブラウザはHTTPからHTTPSにリダイレクトし、SSL/TLSを使用しない限り接続を拒否するようになります。

force_ssl: [hsts: true] を設定すると、Strict-Transport-Security ヘッダーに、ポリシーが有効な期間を定義するmax ageが設定されます。最近のウェブブラウザは、標準的なケースではHTTPからHTTPSにリダイレクトすることでこれに対応しますが、他の影響もあります。HSTSを定義しているRFC6797では、ブラウザはホストのポリシーを追跡し、期限が切れるまでそれを適用すべきであると規定しています。また、80以外のポートからのトラフィックは、ポリシーにしたがって暗号化されているとみなされると規定しています。

これは、https://localhost:4000 などのローカルホスト上のアプリケーションにアクセスした場合、予期せぬ動作をする可能性があります。これは、コンピューター上で実行している他のローカルサーバーやプロキシへのトラフィックを混乱させる可能性があります。localhost上の他のアプリケーションやプロキシは、トラフィックが暗号化されていない限り動作を拒否します。

誤ってlocalhostのHSTSを有効にしてしまった場合、localhostからのHTTPトラフィックを受け入れる前にブラウザのキャッシュをリセットする必要があるかもしれません。Chromeの場合は、デベロッパーツールを起動してリロードアイコンを長押しすると表示されるリロードメニューから、「キャッシュの消去とハード再読み込み」を実行する必要があります。Safariの場合は、キャッシュをクリアし、~/Library/Cookies/HSTS.plist のエントリを削除して(またはファイルを完全に削除して)、Safariを再起動する必要があります。あるいは、force_ssl:expires オプションを 0 に設定することもできます。HSTSのオプションについての詳細はPlug.SSLにあります。