Skip to content

关于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连接**内部而不是纯(未加密)TCP连接中的普通**HTTP**。

Tip

注意,通信的加密发生在**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终止代理关闭期间将不可用。

所有这些续订过程,同时仍然提供应用服务,是您希望使用TLS终止代理来处理HTTPS而不是直接将TLS证书与应用服务器(例如Uvicorn)一起使用的主要原因之一。

总结

拥有**HTTPS**非常重要,并且在大多数情况下非常**关键**。作为开发者,您在HTTPS方面的大部分努力只是关于**理解这些概念**及其工作原理。

但一旦您了解了**开发者HTTPS**的基本信息,您就可以轻松地组合和配置不同的工具,以简单的方式帮助您管理一切。

在接下来的几章中,我将向您展示几个具体的示例,说明如何为**FastAPI**应用程序设置**HTTPS**。🔒