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 | 大幅简化握手,移除不安全算法 | 推荐使用 |
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 会用来派生后续的会话密钥。
3.2.3 握手第三阶段:Change Cipher Spec 与 Finished
双方都确认密钥无误后,客户端发送 Change Cipher Spec 消息,告诉服务端:「接下来我要用协商好的密钥加密通信了」。然后发送 Finished 消息,这条消息是用新密钥加密的,包含之前所有握手消息的哈希值。
服务端收到后,做同样的操作:发送 Change Cipher Spec 和 Finished。客户端验证服务端的 Finished 消息。如果验证通过,握手完成,开始加密数据传输。
3.2.4 握手流程图
下面这张图展示了完整的 TLS 1.2 握手流程。我建议你仔细看看,尤其是密钥交换和证书验证的部分。
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 检查 |
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 调用)非常有用。
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 安全得多。