查看源代码 使用 SSL

要准备应用程序以通过 SSL 提供服务请求,我们需要添加一些配置和两个环境变量。为了使 SSL 能够正常工作,我们需要从证书颁发机构获取密钥文件和证书文件。我们需要配置的环境变量是这两个文件的路径。

配置包括一个新的 https: 键,用于我们的端点,其值是端口、密钥文件路径和证书 (PEM) 文件路径的关键字列表。如果我们添加 otp_app: 键,其值为我们应用程序的名称,Plug 将开始在应用程序的根目录中查找它们。然后,我们可以将这些文件放入我们的 priv 目录中,并将路径设置为 priv/our_keyfile.keypriv/our_cert.crt

以下是在 config/runtime.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: 键,我们需要提供文件在文件系统中的绝对路径,以便 Plug 能够找到它们。

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

https: 键下的选项被传递给 Plug 适配器,通常是 Bandit,它反过来使用 Plug.SSL 来选择 TLS 套接字选项。有关可用选项及其默认值的更多信息,请参阅 Plug.SSL.configure/1 的文档。 Plug HTTPS 指南Erlang/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, MyAppWeb.Endpoint,
  force_ssl: [rewrite_on: [:x_forwarded_proto]]

要动态地重定向到当前请求的 host,请将 :force_ssl 配置中的 :host 设置为 nil

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

在这些示例中, rewrite_on: 键指定了位于应用程序前面的反向代理或负载均衡器使用的 HTTP 标头,以指示请求是通过 HTTP 还是 HTTPS 接收的。有关将 TLS 卸载到外部元素的含义的更多信息,特别是与安全 Cookie 相关的含义,请参阅 Plug HTTPS 指南。请记住,在该文档中传递给 Plug.SSL 的选项应该在 Phoenix 应用程序中使用 force_ssl: 端点选项来设置。

重要的是要注意, force_ssl: 是一个编译时配置,所以它通常是在 prod.exs 中设置的,从 runtime.exs 设置它将不起作用。

HSTS

HSTS 是 "HTTP Strict-Transport-Security" 的缩写,是一种机制,允许网站将自己声明为只能通过安全连接 (HTTPS) 访问。它是为了防止剥离 SSL/TLS 加密的中间人攻击而引入的。HSTS 会导致网络浏览器从 HTTP 重定向到 HTTPS,并且拒绝连接,除非连接使用 SSL/TLS。

设置了 force_ssl: [hsts: true] 后,会添加 Strict-Transport-Security 标头,并带有定义策略有效持续时间的 max-age。现代网络浏览器会对此做出反应,从 HTTP 重定向到 HTTPS,以及其他结果。 RFC6797 定义了 HSTS,它还规定 **浏览器应该跟踪主机的策略,并在策略过期之前应用它。** 它进一步规定 **除 80 端口之外的任何端口上的流量都应假定为已加密**,符合该策略。

虽然建议在生产环境中使用 HSTS,但它可能会导致在访问 localhost 上的应用程序时出现意外行为。例如,使用启用了 HSTS 的 https://localhost:4000 访问应用程序会导致以下情况:来自 localhost 的所有后续流量(端口 80 除外)都应加密。这可能会破坏您计算机上与 Phoenix 应用程序无关的其他本地服务器或代理的流量,并且可能不支持加密流量。

如果您无意中为 localhost 启用了 HSTS,您可能需要重置浏览器的缓存,然后它才会再次接受来自 localhost 的 HTTP 流量。

对于 Chrome

  1. 打开开发者工具面板。
  2. 单击并按住地址栏旁边的重新加载图标以显示下拉菜单。
  3. 选择 "清空缓存并强制重新加载"。

对于 Safari

  1. 清除您的浏览器缓存。
  2. ~/Library/Cookies/HSTS.plist 中删除条目或完全删除该文件。
  3. 重新启动 Safari。

对于其他浏览器,请参考 HSTS 的文档。

或者,在 force_ssl 上将 :expires 选项设置为 0 应该会使条目过期并禁用 HSTS。

有关 HSTS 选项的更多信息,请参阅 Plug.SSL