HTTPS 与 TLS 究竟如何工作:握手过程详解

Security2026-10-21TryQuickToolBox

你添加了证书,浏览器显示了锁图标,然后你就继续了。直到有一天,一个 API 客户端因 SSL_ERROR_RX_RECORD_TOO_LONG 而失败,一个移动应用拒绝你的证书链,或者在启用 HSTS 后出现重定向循环。突然间,HTTPS 感觉像一个黑盒子。

本文打开这个盒子。我们将逐步介绍 TLS 握手期间实际发生的情况,解释证书为何存在,对称加密和非对称加密各自适用于何处,以及如何调试你在生产环境中实际会遇到的故障。

HTTPS 到底是什么

HTTPS 不是独立的协议。它是在 TLS 加密连接上发送的普通 HTTP。URL 方案改变,默认端口从 80 变为 443,其他一切——方法、标头、状态码、Cookie——行为相同。

TLS 在 TCP 之上为你提供三项保证:

第三项保证是人们低估的。没有身份验证的加密毫无价值:能够拦截流量的攻击者可以简单地提供自己的密钥。

TLS 1.3 握手,一步一步来

与 TLS 1.2 相比,TLS 1.3 大幅简化了握手。以下是浏览器和服务器之间的流程:

  1. ClientHello——客户端发送支持的 TLS 版本、密码套件、一个随机值,以及 Diffie-Hellman 组(例如 X25519)的密钥共享。
  2. ServerHello——服务器选择一个密码套件并发送自己的密钥共享。双方现在可以推导出相同的共享密钥。
  3. 加密扩展和证书——服务器发送其证书链,已使用推导出的密钥加密。
  4. Finished——双方发送握手记录的 MAC,以证明没有篡改。
  5. 应用数据——HTTP 请求和响应在建立的会话上流动。

在 TLS 1.3 中,完整握手需要一次往返,而恢复可以达到零往返。TLS 1.2 在应用数据流动之前需要两次往返。

为什么证书在密钥推导后发送

在 TLS 1.3 中,证书是加密的。这意味着被动观察者甚至无法仅从证书看到客户端访问的是哪个域名,从而减少了信息泄露。这也意味着那些期望在第一个数据包中看到明文证书的调试工具会显得困惑。

对称加密与非对称加密

一个常见的误解是 HTTPS 使用服务器的公钥加密所有内容。并非如此。非对称加密速度慢且大小受限;它仅用于建立共享密钥。

阶段加密类型目的
密钥交换非对称(ECDHE)在不发送的情况下就共享密钥达成一致
身份验证非对称签名证明服务器拥有证书密钥
批量数据对称(AES-GCM 或 ChaCha20-Poly1305)为 HTTP 流量提供快速加密和完整性

Diffie-Hellman 密钥交换有一个有价值的特性,称为前向保密:即使服务器的私钥后来泄露,记录的过去会话也无法解密,因为会话密钥从未从该长期密钥派生。

证书里面有什么

X.509 证书将公钥绑定到域名。它包含:

浏览器信任一组固定的根证书颁发机构。你的服务器发送一个中间证书,浏览器从你的叶子证书构建一条链,直到受信任的根。如果中间证书缺失,桌面浏览器可能仍然工作,因为它们缓存中间证书,而移动客户端和命令行工具会失败。这是生产环境中最常见的 TLS 错误。

实用的调试步骤

当 HTTPS 行为异常时,从外到内工作。

  1. 检查证书链,使用 openssl s_client -connect example.com:443 -servername example.com -showcerts。确认服务器发送完整的链,而不仅仅是叶子证书。
  2. 检查过期时间——过期的证书会导致硬故障,没有变通方法。
  3. 检查主机名——SAN 列表必须包含客户端请求的确切主机名,包括任何 www 前缀。
  4. 检查协议版本——如果旧版客户端仅支持 TLS 1.0,而你的服务器要求 TLS 1.2+,握手会在 HTTP 之前失败。
  5. 检查重定向——从 HTTPS 到 HTTP 再返回的重定向会创建循环;在服务器配置中修复,而不是在应用程序代码中。
# 检查证书链和协商的协议
openssl s_client -connect example.com:443 \
  -servername example.com -showcerts < /dev/null 2>&1 | \
  openssl x509 -noout -subject -issuer -dates

# 测试特定的 TLS 版本
openssl s_client -connect example.com:443 -tls1_2 < /dev/null

在 Nginx 中,ssl_certificate 指令应指向一个包含叶子证书后跟中间证书的文件,按此顺序。以错误的顺序连接它们会产生容易遗漏的链错误。

HSTS 和配置错误的代价

HTTP 严格传输安全告诉浏览器在给定时间段内对某个域使用 HTTPS。它很强大且无情:一旦浏览器看到较长的 max-age,即使你后来在服务器上禁用了 HTTPS,它也会拒绝纯 HTTP。

从较短的 max-age 开始,验证一切正常,然后增加它。只有在每个子域都提供有效的 HTTPS 后,才添加 includeSubDomains。预加载实际上是永久的——只有在你确定时才请求它。

常见的错误配置

将证书续订视为部署管道,而不是日历提醒。如果续订需要人工干预,它最终会在最糟糕的时候失败。

常见问题

HTTPS 会加密我访问的域名吗?

域名出现在 TLS SNI 扩展中,通常在网络上可见。路径和查询字符串是加密的,但网络观察者通常可以看到你连接到哪个主机。

为什么我的网站在浏览器中工作,但在 curl 或移动应用中失败?

几乎总是缺少中间证书。浏览器通常从以前的访问中缓存中间证书,而 curl 和许多移动 HTTP 栈要求服务器发送完整的链。

TLS 1.3 总是比 TLS 1.2 更好吗?

对于现代客户端,是的——更少的往返、更小的密码套件集,并且默认前向保密。仅当你仍然服务于无法协商 1.3 的客户端时,才保持 TLS 1.2 启用。

当你需要检查终止 TLS 的服务器的日志时,快速通过 Nginx 日志分析器 可以帮助你在握手失败、重定向循环和不寻常的客户端行为变成中断之前发现它们。