HTTPS 与 TLS 工作原理详解:握手过程全解析
你在 URL 前面输入了 https://,看到了小锁图标,然后继续工作。但当证书在凌晨两点过期、混合内容警告导致结账页面崩溃,或者客户问为什么他们的 API 调用因 TLS 错误而失败时,“小锁意味着安全”就不再是一个有用的答案了。本文解释了 HTTPS 连接在网络上实际发生的过程,以及如何在没有猜测的情况下进行配置。
HTTPS 为 HTTP 增加了什么
HTTPS 是承载在 TLS(传输层安全)会话中的普通 HTTP。TLS 提供三个具体保证:
- 机密性 — 流量被加密,中间人无法读取。
- 完整性 — 传输中的篡改会被检测到,连接会失败。
- 身份验证 — 服务器证明它控制着所提供证书的私钥。
注意列表中没有什么:HTTPS 不说明服务器是否可信、应用程序是否有漏洞,或者数据在静态存储时是否安全。它保护的是管道,而不是端点。
TLS 握手,逐步解析
握手是在任何 HTTP 字节流动之前发生的协商。在 TLS 1.3 中,它只需要一个往返;TLS 1.2 需要两个。以下是 TLS 1.3 流程的通俗解释:
- ClientHello — 客户端发送支持的 TLS 版本、加密套件列表、随机数以及 Diffie-Hellman 密钥交换的密钥共享(通常是 X25519)。
- ServerHello — 服务器选择一个版本和加密套件,返回自己的密钥共享,并发送其证书链以及对握手转录的签名。
- 密钥派生 — 双方从 Diffie-Hellman 交换计算出共享密钥。临时密钥意味着会话密钥是唯一的,并且前向保密成立:以后泄露服务器的长期密钥不会解密已记录的流量。
- Finished — 双方发送对转录的 MAC。如果有任何内容被更改,握手将中止。
- 应用数据 — HTTP 请求和响应使用从共享密钥派生的对称密钥进行加密。
证书是身份验证所在之处。服务器用其私钥对握手进行签名;客户端使用证书中的公钥验证该签名,然后根据受信任的根 CA 验证证书本身,检查主机名、有效期和吊销状态。
对称与非对称:为什么两者都用
非对称加密(RSA 或 ECDSA)速度慢,但解决了密钥分发问题。对称加密(AES-GCM、ChaCha20-Poly1305)速度快,但需要共享密钥。TLS 仅在握手期间使用非对称加密来建立对称会话密钥,然后使用该密钥加密批量数据。这就是现代 TLS 1.3 连接速度快的原因:一个往返,然后其余部分使用廉价的 AES 或 ChaCha20。
加密套件及其真正重要的部分
加密套件是算法的命名捆绑。在 TLS 1.3 中,列表很短,所有选项都被认为是安全的;在 TLS 1.2 中,你必须仔细选择。快速比较:
| 方面 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 握手往返次数 | 2(或 1 次,带恢复) | 1(带恢复的 0-RTT) |
| 密钥交换 | RSA 或 (EC)DHE | 仅临时 (EC)DHE |
| 前向保密 | 可选 | 始终 |
| 传统加密(CBC、RC4) | 如果配置错误则可能 | 已移除 |
实用要点:仅启用 TLS 1.2 和 1.3,禁用 TLS 1.0/1.1,并优先使用 ECDSA 证书以降低握手成本。让你的服务器或反向代理选择套件顺序;在 2024 时代的软件中,大多数默认值是合理的。
如何检查真实连接
你不需要相信文档——验证你自己的端点。openssl s_client 显示协商的版本、加密套件和证书链:
openssl s_client -connect example.com:443 -servername example.com -tls1_3 </dev/null
查看 Protocol 和 Cipher 行,然后检查证书链输出中的过期时间和 SAN 条目。如果链不完整,某些客户端会失败,而浏览器会成功,这是“在 Chrome 中有效但在 curl 中无效”错误的经典来源。
常见的 HTTPS 配置错误
- 缺少中间证书 — 服务器只发送其叶子证书。浏览器可能会获取中间证书,但 API 客户端通常不会。
- 混合内容 — HTTPS 页面通过 HTTP 加载脚本或图像;浏览器会阻止或降级它。使用浏览器控制台进行审计。
- 没有 HSTS — 没有
Strict-Transport-Security,在重定向发生之前,通过 HTTP 的首次请求可能被拦截。 - 薄弱或过时的协议版本 — 为旧客户端保留 TLS 1.0 会悄然削弱每个连接。
- 证书过期 — 自动化续订;手动日历提醒不是策略。
合理的服务器配置
对于 Nginx,一个最小的现代配置块如下所示:
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
fullchain.pem 文件是防止缺少中间证书问题的关键。将端口 80 的重定向放在单独的服务器块中,以便 HTTP 仍可用于 ACME 挑战。
HTTPS 不保护什么
一旦请求到达你的服务器,TLS 就完成了。注入漏洞、访问控制失效、日志中的秘密泄露以及配置错误的 CORS 都位于传输层之上。将 TLS 视为基线,而不是安全计划。如果你正在调试事件,请尽早将传输问题(握手失败、证书过期)与应用程序问题(500 错误、错误负载)分开——它们有完全不同的修复方法。
常见问题
HTTPS 会加密 URL 路径和查询字符串吗?
是的。在 HTTPS 中,整个 HTTP 请求(包括路径和查询字符串)都在加密的 TLS 记录内。但是,除非使用加密客户端问候,否则主机名(SNI)和目标 IP 在握手期间是可见的。
TLS 1.3 总是比 TLS 1.2 快吗?
通常是的,因为它节省了一个往返并支持 0-RTT 恢复。在高延迟连接上差异明显;在本地网络上则较小。更大的优势是 TLS 1.3 完全移除了传统加密选项。
如果我将 HTTP 重定向到 HTTPS,还需要 HSTS 吗?
是的。重定向仅在浏览器到达你的服务器后才有帮助。HSTS 告诉浏览器在未来的请求中完全跳过 HTTP,关闭首次请求可能被拦截的窗口。
当你审计部署并需要快速检查证书详细信息或解码捕获的握手时,请保持基于终端的工作流程——如果你正在为团队记录发现,Markdown 编辑器是将原始 openssl 输出转换为可读报告的快速方法。