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 密钥交换,尽管现在已不推荐):

简化流程:

  1. 客户端发送 ClientHello(支持的 TLS 版本、密码套件列表、随机数)。
  2. 服务端回复 ServerHello(选定版本、密码套件、随机数),并发送自己的证书(内含公钥)。
  3. 客户端验证证书(检查颁发机构、域名、有效期、是否被吊销)。
  4. 客户端生成预主密钥(Pre-Master Secret),用服务端公钥加密后发过去。
  5. 双方各自用相同的算法(PRF)算出主密钥(Master Secret),再派生出会话密钥。
  6. 客户端发 ChangeCipherSpec 表示后续通信加密,然后发 Finished(用会话密钥加密的握手消息摘要)。
  7. 服务端同样发 ChangeCipherSpec + Finished。
  8. 握手完成,之后所有应用数据都受加密保护。

你想想看,这里面每一步出错都会握手失败。我就遇到过某个 CDN 配置了不匹配的证书链,客户端验证不过,直接报 ERR_CERT_AUTHORITY_INVALID。排查了一上午,最后发现是中间证书没上传。

下面这张图帮你把流程可视化一下:

TLS 1.2 握手流程图(简化) 客户端 服务端 ① ClientHello ② ServerHello + 证书 验证证书 ③ 加密的 Pre-Master Secret 计算主密钥 计算主密钥 ④ ChangeCipherSpec + Finished ⑤ ChangeCipherSpec + Finished ✅ 握手完成,加密通信

小提示:如果你用 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 容易遭遇降级攻击,你在安全审计时会被重点问。

好了,基础原理就聊到这儿。记住:历史版本选择关乎安全基线,握手流程是排查故障的“地图”,角色和端口是配置的第一步。后面实战章节我们会在这些地基上搭建各种场景。嗯,先消化这些,有问题翻回来看这张握手图就清晰了。