关于 HTTPS¶
人们很容易认为 HTTPS 只是“启用”或不启用。
但它远比这复杂。
提示
如果你很赶时间或者不关心这些,可以继续阅读后面的章节,了解使用不同技术逐步设置所有内容的说明。
要从消费者的角度了解 HTTPS 的基础知识,请查看 https://howhttps.works/。
现在,从开发者的角度,在考虑 HTTPS 时有几点需要记住:
- 对于 HTTPS,服务器需要有由第三方生成的“证书”。
- 这些证书实际上是从第三方获取的,而不是“生成”的。
- 证书有有效期。
- 它们会过期。
- 然后它们需要续订,再次从第三方获取。
- 连接的加密发生在 TCP 层。
- 这比 HTTP 低一层。
- 因此,证书和加密处理在 HTTP 之前完成。
- TCP 不知道“域名”。它只知道 IP 地址。
- 有关所请求特定域名的信息位于 HTTP 数据中。
- HTTPS 证书“认证”某个特定域名,但协议和加密发生在 TCP 层,在知道正在处理哪个域名之前。
- 默认情况下,这意味着每个 IP 地址只能有一个 HTTPS 证书。
- 无论你的服务器有多大,或者你在上面运行的每个应用程序有多小。
- 然而,有一个解决方案。
- TLS 协议(在 HTTP 之前处理 TCP 层加密的协议)有一个扩展,称为 SNI。
- 此 SNI 扩展允许单个服务器(具有单个 IP 地址)拥有多个 HTTPS 证书并服务多个 HTTPS 域名/应用程序。
- 为了实现这一点,运行在服务器上、监听公共 IP 地址的单个组件(程序)必须拥有服务器中的所有 HTTPS 证书。
- 建立安全连接后,通信协议仍然是 HTTP。
- 即使内容是通过 HTTP 协议发送的,它们也是加密的。
常见的做法是在服务器(机器、主机等)上运行一个程序/HTTP 服务器来管理所有 HTTPS 部分:接收加密的 HTTPS 请求,将解密的 HTTP 请求发送到在同一服务器上运行的实际 HTTP 应用程序(在本例中是 FastAPI 应用程序),从应用程序接收 HTTP 响应,使用适当的 HTTPS 证书对其进行加密,然后使用 HTTPS 将其发送回客户端。这个服务器通常被称为 TLS 终止代理。
你可以用作 TLS 终止代理的一些选项有:
- Traefik(也可以处理证书续订)
- Caddy(也可以处理证书续订)
- Nginx
- HAProxy
Let's Encrypt¶
在 Let's Encrypt 出现之前,这些 HTTPS 证书由受信任的第三方出售。
获取这些证书的过程通常很繁琐,需要大量文书工作,而且证书也相当昂贵。
但后来 Let's Encrypt 诞生了。
它是 Linux 基金会的一个项目。它以自动化方式免费提供 HTTPS 证书。这些证书使用所有标准的加密安全措施,并且生命周期较短(大约 3 个月),因此由于其缩短的生命周期,安全性实际上更好。
域名经过安全验证,证书自动生成。这也允许自动化这些证书的续订。
其理念是自动化这些证书的获取和续订,以便你可以免费、永久地拥有安全的 HTTPS。
开发者的 HTTPS¶
以下是一个 HTTPS API 可能如何逐步实现,主要关注对开发者重要的概念的示例。
域名¶
一切可能从你获取某个域名开始。然后,你会在 DNS 服务器中(可能是你的同一云服务提供商)配置它。
你可能会获得一个云服务器(一个虚拟机)或类似的东西,它会有一个固定的公共 IP 地址。
在 DNS 服务器中,你会配置一条记录(一个“A 记录
”)来将你的域名指向你的服务器的公共 IP 地址。
这通常只需要在第一次设置时做一次。
提示
域名部分远在 HTTPS 之前,但由于一切都依赖于域名和 IP 地址,因此值得在此提及。
DNS¶
现在让我们关注所有实际的 HTTPS 部分。
首先,浏览器会向 DNS 服务器查询域名的 IP,本例中是 someapp.example.com
。
DNS 服务器会告诉浏览器使用某个特定的 IP 地址。这将是你服务器使用的公共 IP 地址,你已在 DNS 服务器中配置。
TLS 握手开始¶
浏览器随后会通过端口 443(HTTPS 端口)与该 IP 地址进行通信。
通信的第一部分只是为了建立客户端和服务器之间的连接,并决定它们将使用的加密密钥等。
客户端和服务器之间为建立 TLS 连接而进行的这种交互被称为 TLS 握手。
带 SNI 扩展的 TLS¶
服务器中只有一个进程可以监听特定 IP 地址的特定端口。同一 IP 地址上的其他端口可能有其他进程监听,但每个 IP 地址和端口的组合只能有一个进程。
TLS(HTTPS)默认使用特定端口443
。所以这是我们需要使用的端口。
由于只有一个进程可以监听此端口,所以执行此操作的进程将是 TLS 终止代理。
TLS 终止代理将能访问一个或多个 TLS 证书(HTTPS 证书)。
使用前面讨论的 SNI 扩展,TLS 终止代理会检查它应该使用哪个可用的 TLS(HTTPS)证书来建立此连接,它会使用与客户端预期域名匹配的证书。
在这种情况下,它会使用 someapp.example.com
的证书。
客户端已经信任生成该 TLS 证书的实体(在本例中是 Let's Encrypt,我们稍后会看到),因此它可以验证证书是否有效。
然后,使用该证书,客户端和 TLS 终止代理决定如何加密其余的 TCP 通信。这完成了 TLS 握手部分。
之后,客户端和服务器就拥有了一个加密的 TCP 连接,这就是 TLS 所提供的。然后他们可以使用该连接开始实际的 HTTP 通信。
这就是 HTTPS,它只是一个安全 TLS 连接中的普通 HTTP,而不是纯(未加密)的 TCP 连接。
提示
请注意,通信的加密发生在 TCP 层,而不是 HTTP 层。
HTTPS 请求¶
现在客户端和服务器(特别是浏览器和 TLS 终止代理)已经建立了加密的 TCP 连接,它们可以开始 HTTP 通信。
因此,客户端发送一个 HTTPS 请求。这只是通过加密的 TLS 连接发送的一个 HTTP 请求。
解密请求¶
TLS 终止代理将使用约定的加密方式来解密请求,并将纯(解密后)HTTP 请求传输给运行应用程序的进程(例如,运行 FastAPI 应用程序的 Uvicorn 进程)。
HTTP 响应¶
应用程序会处理请求,并向 TLS 终止代理发送一个纯(未加密)HTTP 响应。
HTTPS 响应¶
TLS 终止代理随后会使用之前约定的加密方式(以 someapp.example.com
的证书开始)加密响应,并将其发送回浏览器。
接下来,浏览器会验证响应是否有效,并是否使用正确的加密密钥等进行了加密。然后它会解密响应并进行处理。
客户端(浏览器)将知道响应来自正确的服务器,因为它使用了他们之前使用 HTTPS 证书约定的加密方式。
多个应用程序¶
在同一台服务器(或多台服务器)上,可能存在多个应用程序,例如,其他 API 程序或数据库。
只有一个进程可以处理特定的 IP 和端口(在我们的示例中是 TLS 终止代理),但其他应用程序/进程也可以在服务器上运行,只要它们不尝试使用相同的公共 IP 和端口组合。
这样,TLS 终止代理就可以处理多个域名的 HTTPS 和证书,用于多个应用程序,然后将请求转发到每种情况下的正确应用程序。
证书续订¶
在未来的某个时候,每个证书都会过期(大约在获取后 3 个月)。
然后,会有另一个程序(在某些情况下是另一个程序,在某些情况下也可能是同一个 TLS 终止代理)与 Let's Encrypt 通信,并续订证书。
TLS 证书是与域名关联的,而不是与 IP 地址关联的。
因此,要续订证书,续订程序需要向授权机构(Let's Encrypt)证明它确实“拥有”并控制该域名。
为此,并为了适应不同的应用程序需求,有几种方法可以实现。一些流行的方法是:
- 修改一些 DNS 记录.
- 为此,续订程序需要支持 DNS 提供商的 API,因此,根据你使用的 DNS 提供商,这可能是一个选项,也可能不是。
- 在与域名关联的公共 IP 地址上作为服务器运行(至少在证书获取过程中)。
- 正如我们上面所说,只有一个进程可以监听特定的 IP 和端口。
- 这是为什么同一个 TLS 终止代理也负责证书续订过程会非常有用的原因之一。
- 否则,你可能需要暂时停止 TLS 终止代理,启动续订程序以获取证书,然后将它们配置到 TLS 终止代理,然后重新启动 TLS 终止代理。这并不理想,因为在 TLS 终止代理关闭期间,你的应用程序将不可用。
所有这些在仍然服务应用程序的同时进行续订的过程,是你想拥有一个单独的系统来处理 HTTPS,并使用 TLS 终止代理而不是直接使用应用程序服务器(例如 Uvicorn)处理 TLS 证书的主要原因之一。
总结¶
拥有 HTTPS 非常重要,在大多数情况下也相当关键。作为开发者,你围绕 HTTPS 所付出的主要努力只是关于理解这些概念以及它们是如何工作的。
但一旦你了解了开发者的 HTTPS 的基本信息,你就可以轻松地组合和配置不同的工具,以简单的方式管理一切。
在接下来的章节中,我将向你展示几个具体的例子,说明如何为 FastAPI 应用程序设置 HTTPS。 🔒