3、传输层安全协议(TLS/SSL)

聊到通信安全,TLS/SSL 是绕不开的核心。说白了,它就是互联网的「加密管道」。你每次访问 HTTPS 网站、收发加密邮件、甚至玩网游,背后都有它的影子。我入行那会儿,SSL 还是主流,后来才慢慢过渡到 TLS。这中间踩过的坑,今天一并讲清楚。

3.1 TLS/SSL 协议发展史

SSL 是 Netscape 在 90 年代搞出来的。那时候电商刚起步,信用卡号在网络上裸奔,太吓人了。SSL 1.0 从未公开发布,因为漏洞太多。SSL 2.0 在 1995 年发布,但很快被发现存在严重安全问题。我记得有个老项目,用的就是 SSL 2.0,后来被安全审计直接判了死刑。

真正能打的是 SSL 3.0(1996 年)。它奠定了记录协议和握手协议的基础框架。后来 IETF 接手,把它改名为 TLS。TLS 1.0 其实就是 SSL 3.0 的微调版,两者兼容性很好。

发展脉络大致如下:

版本 发布年份 关键变化 现状
SSL 2.0 1995 首个公开版本,但漏洞多 已废弃
SSL 3.0 1996 奠定基础架构,引入握手协议 已废弃(POODLE 攻击)
TLS 1.0 1999 标准化 SSL 3.0,增强 HMAC 已废弃
TLS 1.1 2006 防 CBC 攻击,增加 IV 已废弃
TLS 1.2 2008 支持 AEAD 加密套件,SHA-256 广泛使用
TLS 1.3 2018 大幅简化握手,移除不安全算法 推荐使用
注意: 我曾经在维护一个遗留系统时,发现它还在用 TLS 1.0。客户觉得「能用就行」。结果一次渗透测试,攻击者利用 BEAST 攻击直接解密了会话。从那以后,我建议所有项目至少启用 TLS 1.2。

3.2 TLS 握手过程详解

握手是 TLS 最核心的部分。它负责协商加密算法、交换密钥、验证身份。我习惯把它分成四个阶段来讲。

3.2.1 握手第一阶段:ClientHello 与 ServerHello

客户端先发一个 ClientHello,里面包含:

  • 支持的 TLS 版本列表(比如 TLS 1.2、1.3)
  • 支持的密码套件列表(比如 TLS_AES_128_GCM_SHA256)
  • 一个随机数 ClientRandom
  • Session ID(用于会话复用)

服务端收到后,从列表里挑一个双方都支持的组合,回复 ServerHello:

  • 选定的 TLS 版本
  • 选定的密码套件
  • 服务端随机数 ServerRandom
  • Session ID

嗯,这里要注意:如果双方没有共同支持的版本或套件,握手直接失败。我遇到过几次,原因是客户端太老,服务端又禁用了旧协议。

3.2.2 握手第二阶段:证书与密钥交换

服务端接着发送自己的数字证书(Certificate)。客户端拿到证书后,要验证它是否可信。验证通过后,客户端生成一个 Pre-Master Secret,用服务端的公钥加密,发给服务端。

服务端用自己的私钥解密,拿到 Pre-Master Secret。然后双方各自用 ClientRandom、ServerRandom 和 Pre-Master Secret 计算出相同的 Master Secret。这个 Master Secret 会用来派生后续的会话密钥。

避坑指南: 我曾经遇到一个案例,客户端生成的 Pre-Master Secret 长度不符合规范,导致服务端解密失败。排查了半天,发现是客户端的随机数生成器有 bug。所以,随机数的质量真的很重要。

3.2.3 握手第三阶段:Change Cipher Spec 与 Finished

双方都确认密钥无误后,客户端发送 Change Cipher Spec 消息,告诉服务端:「接下来我要用协商好的密钥加密通信了」。然后发送 Finished 消息,这条消息是用新密钥加密的,包含之前所有握手消息的哈希值。

服务端收到后,做同样的操作:发送 Change Cipher Spec 和 Finished。客户端验证服务端的 Finished 消息。如果验证通过,握手完成,开始加密数据传输。

3.2.4 握手流程图

下面这张图展示了完整的 TLS 1.2 握手流程。我建议你仔细看看,尤其是密钥交换和证书验证的部分。

TLS 1.2 完整握手流程 客户端 服务端 ClientHello (版本、密码套件、随机数) ServerHello (选定版本、套件、随机数) Certificate (服务端数字证书) ServerHelloDone 验证证书 生成 Pre-Master Secret ClientKeyExchange (用服务端公钥加密的 Pre-Master Secret) 解密 Pre-Master Secret 计算 Master Secret ChangeCipherSpec Finished (加密的握手哈希) ChangeCipherSpec Finished

3.3 证书体系与 PKI

证书是 TLS 信任的基石。你想想看,如果没有证书,你怎么确定你连接的服务端不是中间人?PKI(公钥基础设施)就是解决这个问题的。

3.3.1 数字证书的结构

一个 X.509 证书包含以下关键字段:

  • 版本号:通常是 V3
  • 序列号:CA 分配的唯一标识
  • 签名算法:比如 SHA-256 with RSA
  • 颁发者:签发该证书的 CA 名称
  • 有效期:开始和结束时间
  • 主体:证书持有者的信息(域名、组织等)
  • 公钥:持有者的公钥
  • 签名:CA 对证书内容的数字签名

核心要点: 证书验证的核心是「信任链」。你的浏览器或操作系统内置了一批根证书(Root CA)。这些根证书是绝对信任的。如果服务端证书是由某个根 CA 直接签发的,那验证就很简单。如果是中间 CA 签发的,就需要逐级向上验证,直到根证书。这个过程叫「证书链验证」。

3.3.2 证书验证的常见问题

我在项目中遇到过不少证书相关的问题,这里列几个典型的:

问题 原因 解决方法
证书过期 证书超过有效期 续期或重新申请证书
域名不匹配 证书的 CN/SAN 与访问域名不一致 重新签发匹配域名的证书
证书链不完整 服务端未发送中间 CA 证书 配置完整的证书链文件
自签名证书 非 CA 签发的证书,不被信任 导入为信任证书,或使用 CA 签发
吊销未检测 证书已被 CA 吊销,但客户端未检查 CRL/OCSP 启用 OCSP Stapling 或 CRL 检查
避坑指南: 我曾经帮一个客户排查 HTTPS 访问失败的问题。服务端证书明明有效,但客户端就是报错。最后发现是服务端没有配置中间 CA 证书,只发了服务器证书。客户端没有中间证书,无法构建完整的证书链,自然验证失败。所以,配置证书时一定要把完整的证书链(服务器证书 + 中间 CA 证书)一起配置上。

3.4 TLS 1.3 新特性

TLS 1.3 是 2018 年发布的,相比 TLS 1.2,变化非常大。我刚开始用 TLS 1.3 时,最直观的感受就是「快」。握手从 2-RTT 降到了 1-RTT,甚至 0-RTT。

3.4.1 握手简化

TLS 1.3 砍掉了不少冗余步骤。比如,它不再支持静态 RSA 密钥交换,因为那玩意儿不安全。现在只支持 ECDHE(椭圆曲线 Diffie-Hellman 临时密钥交换)。这意味着每次握手都会生成新的临时密钥,即使私钥泄露,也无法解密历史会话。这就是所谓的「前向安全性」。

握手流程也精简了:

  • 客户端直接发送支持的密码套件和密钥交换参数(比如 ECDHE 的公钥)
  • 服务端回复选定的套件、自己的密钥参数和证书
  • 双方直接计算出共享密钥,握手完成

说白了,TLS 1.3 把原来需要两次往返的协商,压缩到了一次往返。

3.4.2 0-RTT 模式

对于之前连接过的客户端,TLS 1.3 支持 0-RTT 模式。客户端可以在第一个数据包中就携带应用数据,不需要等待握手完成。这对延迟敏感的应用(比如 HTTP/2、移动端 API 调用)非常有用。

注意: 0-RTT 存在重放攻击的风险。攻击者可以截获 0-RTT 数据包,然后重复发送。所以,0-RTT 只适合幂等操作(比如 GET 请求),不适合非幂等操作(比如转账)。我建议你在启用 0-RTT 时,一定要在应用层做好重放防护。

3.4.3 移除不安全算法

TLS 1.3 彻底移除了一堆不安全的算法:

  • RSA 密钥交换(无前向安全性)
  • DH 密钥交换(无前向安全性)
  • CBC 模式加密(易受 padding oracle 攻击)
  • RC4 流密码(弱密钥)
  • 3DES(块大小太小)
  • SHA-1 哈希(碰撞攻击)

现在 TLS 1.3 只支持 AEAD 加密套件,比如 TLS_AES_128_GCM_SHA256 和 TLS_CHACHA20_POLY1305_SHA256。这些套件同时提供加密和完整性校验,安全性更高。

3.4.4 配置示例

下面是一个 Nginx 配置示例,只启用 TLS 1.3 和 TLS 1.2,并禁用不安全的密码套件:

server {
    listen 443 ssl;
    server_name example.com;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
    ssl_prefer_server_ciphers on;

    ssl_certificate /etc/ssl/certs/example.com.pem;
    ssl_certificate_key /etc/ssl/private/example.com.key;

    # 启用 OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;
}

总结: TLS 1.3 是当前最推荐的 TLS 版本。它更快、更安全、更简洁。如果你还在用 TLS 1.2 或更早的版本,我建议尽快升级。升级过程中要注意兼容性,确保客户端也支持 TLS 1.3。不过,即使只升级到 TLS 1.2,也比用 TLS 1.0/1.1 安全得多。