看图学 HTTPS
Category(分类): NET Protocol Status: 未知
前言
之前说到 HTTPS,我的概念中只有“更安全”和“需要服务器配置证书”,但对 HTTPS 为什么更安全、整套流程如何实现没有具体概念。因此,本文通过一幅幅流程图,对 HTTP 到 HTTPS 的演变过程进行入门介绍。
本文中的图片和示意流程主要用于帮助理解。真实 TLS 的握手过程会根据 TLS 版本、密码套件、会话恢复和实现方式有所不同,不能把所有示意图直接当作 TLS 1.3 的完整报文流程。
本文也会同步到个人网站。
正文
HTTP 是什么样的?
HTTP 属于应用层协议。HTTP/1.1 通常运行在 TCP 之上,TCP 负责可靠的字节流传输,IP 负责寻址和路由;HTTP/2 也通常运行在 TCP 之上,而 HTTP/3 则运行在 QUIC 之上。HTTP 主要规定请求方法、状态码、首部和内容等语义。
最简单的 HTTP 通信过程如下:

客户端发出请求,服务器返回响应。在直接使用 http:// 时,HTTP 内容通常以明文形式传输,网络中的中间人可能读取、篡改或伪造数据,造成信息泄露和请求劫持。
HTTP 本身不提供加密和身份认证,但 HTTP 可以运行在 TLS 之上,这就是 HTTPS。
加密可以解决问题吗?
因为明文传输不安全,一个自然的想法是在发送前对数据进行加密:

这种加密方式叫作对称加密。对称加密使用同一个秘密密钥进行加密和解密。实际 TLS 常用 AES-GCM、ChaCha20-Poly1305 等 AEAD 算法,它们同时提供机密性和完整性保护。
但仅仅“加密”还不够,还需要解决密钥如何安全地交给通信双方,以及客户端如何确认自己连接的确实是目标服务器。
多个客户端怎么办?
假设服务器和所有客户端都使用同一个密钥:

这种方式显然不合理:一旦某个客户端的密钥泄露,其他客户端的通信也可能受到影响。实际系统通常不会让所有用户长期共用同一个应用数据密钥,而是为连接建立独立的会话密钥。
如果为每一个客户端或每一条连接使用不同的密钥,可以降低密钥泄露的影响范围:

但新的问题是:客户端和服务器如何安全地得到这把会话密钥?
对称加密密钥如何传输?
如果一端生成对称密钥,再通过普通 HTTP 直接传输给另一端:

中间人就可能截获密钥并解密后续数据。对称密钥再用另一个没有安全传输渠道的对称密钥加密,会陷入“新的密钥如何传输”的循环。
解决这个问题不能只依靠不断套用对称加密,而需要使用经过身份认证的密钥协商机制。早期 TLS 可以使用 RSA 密钥交换,现代 TLS 1.3 通常使用临时 Diffie-Hellman(ECDHE)协商共享秘密。
非对称加密
非对称密码学使用一对密钥:公钥和私钥。公钥可以公开,私钥必须由持有者妥善保管。
以 RSA 为例,公钥加密的内容通常由对应私钥解密。但现代密码学还要区分另一种用途:
- 公钥加密、私钥解密:主要用于机密性;
- 私钥签名、公钥验证:主要用于身份认证和完整性。
“私钥加密、公钥解密”是对数字签名的简化说法,不应当把数字签名当成普通加密。RSA 只是非对称密码学的一类算法,现代 TLS 的密钥协商还常使用基于椭圆曲线的 ECDHE。

在传输服务器公钥的过程中,中间人确实可以看到公钥。看到公钥本身并不危险,因为公钥本来就可以公开;真正的问题是客户端如何确认拿到的公钥属于目标服务器,而不是属于中间人。
中间人攻击
MITM 是 Man-in-the-Middle Attack 的缩写,中文称为中间人攻击。
如果客户端只接收网络上传来的公钥,而不验证公钥的身份,中间人可以把服务器公钥替换成自己的公钥:

客户端误以为这是服务器的公钥,并使用它加密数据。中间人就可以用自己的私钥解密数据,再使用真正服务器的公钥重新加密并转发,客户端和服务器都可能意识不到通信已经被监听。
因此,HTTPS 需要在密钥协商之外增加服务器身份认证。
第三方认证:证书与数字签名
在 HTTPS 中,服务器通常通过 X.509 证书 + 数字签名 向客户端证明自己的身份。

证书不是简单的“把网站信息加密后发给客户端”。一张证书通常包含:
- 服务器公钥;
- 域名身份信息,现代证书主要通过 Subject Alternative Name(SAN)表示;
- 有效期;
- 证书序列号和签发者;
- Key Usage、Extended Key Usage 等扩展;
- CA 对证书内容的数字签名。
CA 会对待签名证书内容计算摘要,然后使用 CA 私钥生成数字签名。这里不能使用 MD5 作为现代安全签名算法,MD5 已经不适合安全用途,实际系统通常使用 SHA-256 等安全哈希算法配合数字签名算法。
数字证书可以简化理解为:
证书 = 服务器身份信息 + 服务器公钥 + CA 数字签名 + 其他扩展字段
如果中间人拦截证书并替换服务器公钥,由于没有 CA 的私钥,通常无法为修改后的证书生成有效的 CA 签名,客户端验证证书链时就会失败。

浏览器、操作系统或应用程序会预置一些信任根证书。客户端不是“解密证书”,而是使用信任库中的 CA 公钥验证签名,并检查:
- 证书链能否连接到本地信任的根 CA;
- 当前访问的域名是否匹配证书的 SAN;
- 证书是否在有效期内;
- 证书是否允许用于服务器身份认证;
- 证书是否被撤销或被本地安全策略禁止使用。
服务器通常发送终端证书和中间 CA 证书,根证书一般已经在客户端信任库中,不需要服务器重复发送。
为什么要有数字签名?
CA 是一个可以为多个网站签发证书的机构。中间人也可以申请证书,但 CA 必须验证申请者对域名的控制权或组织身份,不能只因为“申请了”就签发任意域名的证书。
如果没有 CA 的数字签名,任何人都可以声称某个公钥属于某个网站,客户端无法判断证书内容是否真的得到可信机构认可:


数字签名的作用不是隐藏证书内容,而是证明证书内容确实由对应 CA 签发,并且在签发后没有被修改。公钥本身可以公开,证书也可以被任何人读取,但攻击者不能仅凭读取证书就伪造有效签名。
如果用户手动安装了攻击者控制的根证书,或者操作系统、浏览器的信任库被修改,攻击者就可能重新建立一条客户端信任的证书链。因此,不要随便安装来源不明的根证书,也不要忽略浏览器的证书错误。
对称加密与 TLS 会话密钥
在较早的 TLS 1.2 RSA 密钥交换示意中,客户端可能生成 premaster secret,并使用服务器证书中的 RSA 公钥加密后发送给服务器,双方再从中派生会话密钥:

但这不是现代 TLS 1.3 的完整流程。TLS 1.3 通常使用 ECDHE:
- 客户端在
ClientHello中发送支持的版本、密码套件、随机数和密钥交换参数; - 服务器在
ServerHello中发送自己的密钥交换参数,并发送证书和CertificateVerify; - 客户端验证证书链、域名和服务器的握手签名;
- 客户端和服务器分别计算相同的 ECDHE 共享秘密;
- 双方通过 HKDF 派生 TLS 记录层使用的会话密钥;
- 后续
Application Data使用 AES-GCM 或 ChaCha20-Poly1305 等对称 AEAD 算法传输。
最终的应用数据密钥通常不会直接在网络上传输。临时 ECDHE 还可以提供前向保密:即使服务器长期证书私钥日后泄露,也不能直接解密过去捕获的 TLS 1.3 1-RTT 会话数据。
整体流程图
更准确的表达是:
HTTPS = HTTP over TLS
TLS 位于 HTTP 和传输层之间。对于 HTTP/1.1 和 HTTP/2,底层通常是 TCP;HTTP/3 则运行在 QUIC 之上。

一次新的 TLS 1.3 HTTPS 连接大致包含以下过程:
客户端 服务器
| -------- ClientHello --------------> |
| <------- ServerHello --------------- |
| <------- Certificate --------------- |
| <------- CertificateVerify --------- |
| <------- Finished ------------------ |
| -------- Finished ----------------> |
| <====== 加密的 HTTP Application Data ======> |
实际 TLS 1.3 报文会根据密钥交换、客户端认证、会话恢复和实现方式变化。TLS 1.2 的握手流程也与 TLS 1.3 不同,不能把一张简化图片当成所有版本的固定流程。

HTTPS 的性能影响
HTTPS 会增加一定的握手和计算开销,但不能简单说“HTTPS 一定很慢”:
- TCP 三次握手通常按约 1 个 RTT 估算;
- TLS 1.2 完整握手通常还需要约 2 个 RTT;
- TLS 1.3 完整握手通常还需要约 1 个 RTT;
- 持久连接可以复用已经建立的 TCP + TLS 连接;
- TLS 会话恢复可以减少握手往返;
- TLS 1.3 0-RTT 可以发送早期数据,但存在重放风险,只适合安全幂等的操作;
- 对称加密通常比非对称运算高效,现代 CPU 也常有硬件加速。
HTTP/2 通常通过 TLS 的 ALPN 在握手阶段协商。HTTPS 负责 TLS 安全传输,HTTP/2 负责二进制分帧和多路复用,它们属于不同层次,不能把“HTTPS”和“HTTP/2”看成同一个协议。
总结
HTTPS 的核心不是简单地“客户端拿到服务器公钥,再把一个对称密钥发送给服务器”,而是由多个机制共同完成:
- X.509 证书和 CA 数字签名验证服务器身份;
- ECDHE 等密钥交换机制让双方得到共同秘密,而不直接传输最终会话密钥;
- HKDF 等密钥派生机制生成 TLS 会话密钥;
- AES-GCM、ChaCha20-Poly1305 等 AEAD 算法保护应用数据的机密性和完整性;
- TLS Finished 消息验证握手过程没有被篡改。
HTTPS 主要保护客户端和服务器之间的传输过程,不能替代应用层的身份认证、权限控制、输入校验和端点安全。