TLS/SSL 基础原理
说起 TLS,很多朋友第一反应是“加密”。其实它远不止是加密,它解决的是三个问题:你是谁?你们之间传的东西有没有被改?你们之间传的内容别人能不能看到?说白了,身份认证 + 完整性 + 机密性。
我入行那会儿还是 SSL 3.0 的时代,配个 HTTPS 得自己编译 OpenSSL,坑踩得那叫一个扎实。现在想想,很多基础概念那时就没真搞明白,后来补课才连成体系。
历史演进——从 SSL 到 TLS 的蜕变
这个故事从 1995 年 Netscape 公司开始。他们搞了个安全套接层协议,SSL 1.0 从未公开,因为漏洞太多。真正面世的是 SSL 2.0(1995 年),但它有严重缺陷——比如不能防中间人攻击、MAC 强度不够。我早年项目里见过还在跑 SSL 2.0 的老设备,吓得我连夜写升级方案。
SSL 3.0(1996 年)算是里程碑,完善了握手流程,引入了更可靠的 MAC。可惜后来密码学理论进步,发现它仍然容易受 POODLE 攻击。嗯,这里要注意:SSL 3.0 现在已被 RFC 7568 明确禁止使用。
1999 年,IETF 接手并将其标准化为 TLS 1.0(RFC 2246)。其实改了个名字,内核跟 SSL 3.0 差别不大,但引入了 HMAC 等改进。再往后是 TLS 1.1(2006 年),主要防 CBC 攻击,加了显式 IV。我个人觉得它是个过渡版本,用的企业不多。
TLS 1.2(2008 年)是至今最广泛使用的版本。它引入了 AEAD 加密套件、SHA-256 哈希、扩展主密钥等,可扩展性极强。一直到 2018 年,TLS 1.3(RFC 8446)才发布,这是革命性的——握手从 2-RTT 降到 1-RTT(甚至 0-RTT),去掉了不安全算法(如 RSA 密钥交换、CBC 模式、RC4),强制使用前向安全机制。
我常跟团队说:如果新系统还支持 TLS 1.0/1.1,那就等于给黑客留了后门。你在配置时,请务必将最低版本设为 TLS 1.2。
| 协议版本 | 发布年份 | 关键改进 | 当前状态 |
|---|---|---|---|
| SSL 2.0 | 1995 | 首次公开 | 禁止使用 |
| SSL 3.0 | 1996 | 更完善的握手 | 禁止使用 |
| TLS 1.0 | 1999 | HMAC、标准化 | 弃用(主流浏览器已拒绝) |
| TLS 1.1 | 2006 | 显式 IV、抗 CBC 攻击 | 弃用 |
| TLS 1.2 | 2008 | AEAD、SHA-256、扩展主密钥 | 广泛使用(推荐) |
| TLS 1.3 | 2018 | 1-RTT 握手、前向安全、移除不安全算法 | 逐步普及(强烈推荐) |
注意:你的服务器如果还在使用 SSL 3.0 或 TLS 1.0,请立即行动。我曾经帮一家金融客户做安全审计,他们后台接口居然还接受 SSL 3.0 连接,后来被漏洞扫描直接标红。升级到 TLS 1.2 后,扫描一次性通过。
握手协议流程——一次安全会话是怎么建立的?
TLS 握手,说白了就是双方互相确认身份、协商加密参数、生成会话密钥的过程。我习惯把握手分成两个阶段:握手前和握手后。
举个最常见的 TLS 1.2 握手例子(RSA 密钥交换,尽管现在已不推荐):
简化流程:
- 客户端发送
ClientHello(支持的 TLS 版本、密码套件列表、随机数)。 - 服务端回复
ServerHello(选定版本、密码套件、随机数),并发送自己的证书(内含公钥)。 - 客户端验证证书(检查颁发机构、域名、有效期、是否被吊销)。
- 客户端生成预主密钥(Pre-Master Secret),用服务端公钥加密后发过去。
- 双方各自用相同的算法(PRF)算出主密钥(Master Secret),再派生出会话密钥。
- 客户端发
ChangeCipherSpec表示后续通信加密,然后发Finished(用会话密钥加密的握手消息摘要)。 - 服务端同样发
ChangeCipherSpec+Finished。 - 握手完成,之后所有应用数据都受加密保护。
你想想看,这里面每一步出错都会握手失败。我就遇到过某个 CDN 配置了不匹配的证书链,客户端验证不过,直接报 ERR_CERT_AUTHORITY_INVALID。排查了一上午,最后发现是中间证书没上传。
下面这张图帮你把流程可视化一下:
小提示:如果你用 Wireshark 抓包,可以看到握手阶段每个包的详细内容。我曾用这个方法定位过一个“握手协商到 RC4 算法”的怪异问题——最后发现是服务端密码套件列表里稀里糊涂放了个 RC4。
角色与端口——谁在说话?在哪个门口说?
TLS 通信中只有两个核心角色:客户端(主动发起连接)和服务端(被动等待连接)。但注意,服务端必须持有证书(除非使用 PSK 或自签名,生产环境极少用)。
端口方面有个约定俗成的规则:
- HTTPS:TCP 443(最常用,浏览器默认)
- SMTPS:TCP 465(邮件发送)
- IMAPS:TCP 993(邮件接收)
- POP3S:TCP 995(邮件接收)
- LDAPS:TCP 636(目录服务)
- DTLS:基于 UDP,端口号根据应用场景自定义(如 SIP over DTLS 用 5061)
不过你完全可以给任意 TCP 服务套上 TLS,比如数据库、Redis、消息队列。我记得有个项目要求 MySQL 公网访问,直接裸连风险太高,我们就在 3306 上套了 TLS,配合证书认证才放行。
一句话总结角色分配:
客户端验证服务端证书(单向认证),服务端可选验证客户端证书(双向认证/mTLS)。端口只是一个数值,TLS 的“位置”在传输层之上、应用层之下,它不关心你用的是 443 还是 8443。
还有一个容易混淆的概念:STARTTLS vs TLS 直接封装。STARTTLS 是先建立明文连接,再通过命令升级成 TLS;而常见的 HTTPS(端口 443)是一开始就加密。我建议新系统一律使用“直接 TLS”,STARTTLS 容易遭遇降级攻击,你在安全审计时会被重点问。
好了,基础原理就聊到这儿。记住:历史版本选择关乎安全基线,握手流程是排查故障的“地图”,角色和端口是配置的第一步。后面实战章节我们会在这些地基上搭建各种场景。嗯,先消化这些,有问题翻回来看这张握手图就清晰了。