跳到正文

HTTP 与 HTTPS:TLS 1.3、证书链与排障 ​

一句话:HTTPS 就是 HTTP 跑在 TLS 上,用非对称密钥交换协商会话密钥、用对称加密传数据;它换来机密性、完整性和服务器身份,代价是握手延迟、证书运维,以及中间设备对流量可见性的下降。

适用:TLS 1.2 / 1.3(RFC 5246 / RFC 8446),OpenSSL 1.1.1+(支持 TLS 1.3),curl 7.52+ 配合 OpenSSL 1.1.1 支持 TLS 1.3。端口 80/443 只是约定,不是协议强制。

为什么需要 ​

HTTP 明文传输,同一路径上的代理、NAT、Wi-Fi 和镜像都能读、能改,客户端也无法确认对面是不是目标服务器。HTTPS 要解决三件事:

  • 机密性:链路上看不到明文。
  • 完整性:内容被改动会被发现。
  • 身份认证:客户端能确认自己连的是证书对应的服务器,而不是中间人。

为什么不能只用一种加密:非对称加密慢,适合协商密钥,不适合传大流量;对称加密快,但双方得先有同一把密钥,而「怎么把密钥安全送到对方」本身就需要非对称或密钥交换。所以 HTTPS 采用混合加密:握手阶段用非对称/密钥交换,数据传输用对称加密。

核心机制 ​

一次 HTTPS 连接 ​

先完成 TCP 三次握手,再做 TLS 握手,之后才发送 HTTP 报文。TLS 握手做四件事:协商版本和套件、验证服务器身份、交换密钥材料、生成会话密钥。

TLS 1.2 与 1.3 的差别 ​

维度TLS 1.2TLS 1.3
完整握手往返2-RTT1-RTT
密钥交换RSA 或 (EC)DHE证书握手只允许 (EC)DHE(恢复可用 PSK)
对称算法CBC、GCM、ChaCha20-Poly1305 等只允许 AEAD(GCM、ChaCha20-Poly1305)
前向保密ECDHE 才有,RSA 密钥交换没有证书握手强制具备
握手加密范围ServerHello、Certificate 等明文ServerHello 之后全部加密
旧算法仍可协商 RC4、3DES、SHA-1 等已移除
会话恢复Session ID / Session TicketPSK + 0-RTT(有重放风险)

TLS 1.3 的 1-RTT 流程:

ClientHello (supported_versions, key_share, SNI, ALPN)
  -> ServerHello (key_share)
  <- {EncryptedExtensions, Certificate, CertificateVerify, Finished}
  -> {Finished}
  -> 应用数据

{} 表示已加密。TLS 1.2 的 Certificate 在明文里,TLS 1.3 把它挪进加密段,中间设备能看到的更少。0-RTT 允许恢复连接时在第一个包里带应用数据,但早期数据可能被重放,只应对幂等请求开启。

证书链(certificate chain)验证 ​

服务器通常发送「叶子证书 + 中间 CA 证书」,不发送根证书;根证书预置在客户端信任库。客户端从叶子往上验证签名,直到某个受信任的根,同时检查:

  • 签名链完整、每张证书都在有效期内;
  • 证书的 SAN(Subject Alternative Name)包含访问的域名,CN 已不再被依赖;
  • 扩展密钥用法(EKU)包含 serverAuth;
  • 吊销状态(CRL / OCSP / OCSP stapling)。

服务器在同一个 IP 上托管多个域名时,靠 ClientHello 里的 SNI 选择证书。TLS 1.2 中 SNI 明文;TLS 1.3 默认也明文,ECH(Encrypted Client Hello)能加密但部署仍有限,所以「用了 HTTPS 就看不到域名」并不成立。

前向保密 ​

ECDHE 每次握手生成临时密钥,服务器私钥泄露也无法解密过去的会话,这叫前向保密(forward secrecy)。RSA 密钥交换把会话密钥用服务器公钥加密,私钥一旦泄露,历史流量可被解密,所以 TLS 1.3 直接删掉了它。

最小上手 ​

用 OpenSSL 自签一张证书,跑一个 TLS 服务,再验证链(OpenSSL 1.1.1+):

bash
openssl req -x509 -newkey rsa:2048 -nodes -days 1 \
  -keyout key.pem -out cert.pem -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost"
openssl s_server -accept 8443 -cert cert.pem -key key.pem -www &
openssl s_client -connect 127.0.0.1:8443 -servername localhost \
  -CAfile cert.pem -verify_return_error </dev/null

期望输出包含 Verify return code: 0 (ok) 和 Protocol : TLSv1.3。去掉 -CAfile 再跑一次,会得到 self-signed certificate(返回码 18),说明验证确实发生了;不加 -verify_return_error 时,s_client 即使验证失败也会把连接做完。

看真实站点的证书链和协议:

bash
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
openssl s_client -connect example.com:443 -servername example.com -tls1_3 </dev/null \
  | grep -E 'Protocol|Cipher'
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

本地证书文件用 openssl verify -CAfile root.pem -untrusted intermediate.pem leaf.pem;-untrusted 提供中间证书,-CAfile 只放信任根。

生产动作 ​

  • 到期监控:按续期周期留出至少一个完整续期窗口(例如 30 天)告警;用 ACME / cert-manager 自动续期,续期后要 reload 或热加载证书,否则文件换了进程还在用旧的。用上面的 openssl x509 -enddate 做探针。
  • 证书链完整性:服务器必须把中间证书一起发出。浏览器可能通过 AIA 自动补链,Java、Go、curl 通常不会,表现为「浏览器正常,客户端报 unable to find valid certification path」。用 -showcerts 数链,或在客户端机器上复现。
  • 协议与套件:最低 TLS 1.2,优先 1.3;禁用 TLS 1.0/1.1、RC4、3DES、CBC。用 openssl ciphers -v 'DEFAULT' 看本机策略,或 nmap --script ssl-enum-ciphers -p 443 host 从外部核对。
  • HSTS:Strict-Transport-Security: max-age=31536000; includeSubDomains。先上短 max-age 验证,再逐步加长;一旦把错误配置推给浏览器,回滚期内用户会被锁在 HTTPS 上。
  • OCSP stapling:由服务器代取 OCSP 响应并随握手下发,减少客户端外联和隐私泄露;配置失败时通常 soft-fail,等于没生效,要在服务端日志里确认 stapling 成功。
  • 0-RTT:只对 GET 等幂等请求开启;在 LB/WAF 层做重放防护,否则攻击者可以重放早期请求。
  • TLS 卸载点:在 LB 终止 TLS 时,LB 到后端可能是明文;合规场景要后端 TLS 或 mTLS,并确认 X-Forwarded-Proto 正确传递,避免应用生成错误的跳转链接。

排障按「现象 → 命令 → 判断」:

  • 浏览器报证书错误 → openssl s_client -showcerts -servername <域名> → 看 Verify return code、链长度、SAN、有效期;返回码 10(过期)、18(自签)、20(无法取得本地签发者)、21(链不完整)分别对应不同动作。
  • 浏览器正常,Java/Go 客户端失败 → 在客户端侧抓证书链 → 多数是缺中间证书或客户端不信任该根。
  • 握手慢 → curl -w '%{time_connect} %{time_appconnect}\n' -o /dev/null -s https://host → time_appconnect - time_connect 是 TLS 耗时;反复全握手说明会话复用没生效,或 OCSP 外联超时。

边界 ​

  • HTTPS 不隐藏目标 IP、端口、流量大小和时序;SNI 在多数部署中仍是明文,ECH 尚未普及。
  • 证书只证明「私钥持有者与证书主体匹配」,不证明网站可信;钓鱼站也能拿到有效证书。
  • 在 LB 终止 TLS 后,内网段可能是明文;「入口用了 HTTPS」不等于全链路加密。
  • 自签名/私有 CA 适合内网,但必须把根证书分发到所有客户端信任库,运维成本只是转移,没有消失。
  • 0-RTT 有重放风险;会话票据泄露会削弱前向保密,票据密钥要轮换。
  • 加解密开销通常小于业务处理,尤其长连接和会话复用之后;「HTTPS 慢」很少是拒绝上 TLS 的正当理由,首次握手和证书链才是主要成本。

讲清楚 ​

HTTPS 就是 HTTP 加一层 TLS。TLS 先用非对称密钥交换协商出对称会话密钥,TLS 1.3 的证书握手只允许 (EC)DHE 这类临时密钥交换,所以天然具备前向保密;之后用 AES-GCM 或 ChaCha20-Poly1305 加密数据,兼顾安全和大流量性能。身份靠证书链:服务器发叶子证书和中间证书,客户端一路验签到本地信任的根,同时检查域名、有效期和用途。TLS 1.3 把完整握手压到 1-RTT,删掉了 RSA 密钥交换和 CBC 等旧算法,ServerHello 之后的消息全部加密;代价是 0-RTT 恢复有重放风险。运维上最常翻车的不是算法,而是证书过期和链不完整:浏览器会自动补中间证书,Java、Go、curl 不会。排障第一步永远是 openssl s_client,先看 verify code、链长度、协议版本和有效期,再谈性能。

把一门技术讲透到既懂也会用