TLS协议基础回顾:握手协议、记录协议、TLS 1.2 vs TLS 1.3核心差异、常见TLS库

聊SBOM之前,咱们得先把TLS这块地基夯实了。说实话,我见过太多团队在生成SBOM时,连TLS版本都填错,或者把OpenSSL和BoringSSL的依赖关系搞混。这章我们就快速过一遍TLS的核心机制,重点放在后面做SBOM分析时真正用得上的知识点。

TLS握手协议:连接是怎么建立的?

TLS握手,说白了就是客户端和服务器在正式传输数据之前,先互相打个招呼、亮个身份、商量好用什么加密方式。我刚开始接触TLS时,总觉得握手过程很玄乎,后来自己抓包看了几次,其实就那么几个关键步骤。

一个典型的TLS 1.2握手流程大致如下:

  1. ClientHello:客户端告诉服务器它支持的TLS版本、密码套件列表、以及一个随机数。
  2. ServerHello:服务器从客户端提供的列表中选一个密码套件,也发一个随机数。
  3. ServerCertificate:服务器把自己的数字证书发给客户端,里面包含公钥。
  4. ServerHelloDone:服务器说“我这边信息发完了”。
  5. ClientKeyExchange:客户端生成一个预主密钥,用服务器的公钥加密后发过去。
  6. ChangeCipherSpec:双方通知对方,接下来要开始用协商好的密钥加密通信了。
  7. Finished:双方发送一个验证消息,确认握手过程没有被篡改。

关键点:握手过程中交换的随机数和预主密钥,最终会通过伪随机函数(PRF)生成会话密钥。这个密钥就是后续记录协议加密数据的根本。

我在项目中遇到过一个问题:某个老旧系统只支持TLS 1.0,但安全扫描报告里却显示它用了TLS 1.2的密码套件。后来排查发现,是中间件配置写错了,握手时客户端和服务器协商到了TLS 1.0,但密码套件列表是从TLS 1.2的配置里抄过来的。这种不一致,在SBOM里如果不仔细核对版本号,很容易埋坑。

TLS记录协议:数据是怎么传输的?

握手协议搞定密钥之后,真正的数据传输就交给记录协议了。记录协议做的事情其实很纯粹:把上层数据(比如HTTP请求)切分成合适大小的片段,然后压缩(可选)、加MAC(消息认证码)、加密,最后加上记录头发出去。

记录协议的结构很简单:

记录头(5字节):
  - 内容类型(1字节):20=ChangeCipherSpec, 21=Alert, 22=Handshake, 23=Application Data
  - 协议版本(2字节):例如 0x0303 表示TLS 1.2
  - 长度(2字节):负载数据的长度

负载数据:
  - 压缩后的数据(如果启用了压缩)
  - MAC(消息认证码)
  - 填充(如果使用块加密)

嗯,这里要注意:TLS 1.3之后,记录协议的头格式其实有变化,但核心思想没变——先认证再加密。我习惯把这个顺序记成“先锁门再贴封条”,顺序反了就容易出安全问题。

实战经验:在做SBOM分析时,记录协议本身很少直接出现在依赖清单里。但如果你用的TLS库版本有记录协议层面的漏洞(比如早期的BEAST攻击),那SBOM里就必须标记出来。我曾经帮一个客户排查性能问题,发现他们用的OpenSSL 1.0.1版本在记录协议层有个已知的填充预言攻击漏洞,但SBOM里完全没提——这就是典型的依赖信息缺失。

TLS 1.2 vs TLS 1.3:核心差异在哪?

这两个版本的区别,我经常用一句话概括:TLS 1.3把握手从“两轮半”砍成了“一轮”,而且砍掉了一堆不安全的密码套件。你想想看,TLS 1.2光是密码套件就有好几十种,TLS 1.3直接精简到5种。

具体差异我列个表,这样更清楚:

对比维度 TLS 1.2 TLS 1.3
握手轮次 2-RTT(两轮往返) 1-RTT(一轮往返),支持0-RTT
密码套件数量 几十种,包含大量遗留算法 5种,只保留AEAD加密
密钥交换 支持RSA、DH、ECDH等多种 仅支持(EC)DHE,前向安全性强制
签名算法 在证书中协商 在握手消息中独立协商
会话恢复 Session ID / Session Ticket PSK + 0-RTT
压缩 支持(但已废弃) 不支持
ChangeCipherSpec 显式消息 隐式处理
协议协商 通过ClientHello/ServerHello 通过supported_versions扩展

避坑指南:我曾经在迁移一个金融系统时,发现他们的SBOM里写着“支持TLS 1.2和TLS 1.3”,但实际测试发现TLS 1.3的0-RTT功能根本没启用。更糟的是,他们用的BoringSSL版本对0-RTT的重放保护有缺陷。所以,SBOM里光写版本号是不够的,还得标注启用了哪些特性。

为什么TLS 1.3要砍掉那么多东西?说白了,就是TLS 1.2时代积累的“技术债”太多了。RSA密钥交换没有前向安全性,CBC模式加密容易受padding oracle攻击,压缩功能会引发CRIME攻击……这些在TLS 1.3里全被干掉了。我个人习惯在评估一个系统的TLS安全性时,先看它支不支持TLS 1.3,如果不支持,再看它禁用了哪些TLS 1.2的弱密码套件。

常见TLS库:OpenSSL、BoringSSL、NSS

说到TLS库,这其实是SBOM里最容易出问题的地方。因为很多项目不是直接依赖TLS库,而是通过curl、nginx、Python的requests库间接依赖的。你SBOM里可能只写了curl 7.68.0,但curl背后链接的是哪个TLS库、什么版本,往往被忽略了。

我简单对比一下这三个主流库:

特性 OpenSSL BoringSSL NSS
维护方 OpenSSL团队 Google Mozilla
许可证 OpenSSL/SSLeay双许可 OpenSSL + 额外条款 MPL 2.0
API兼容性 稳定,向后兼容 部分不兼容OpenSSL 独立API
FIPS支持 有FIPS模块 无官方FIPS 有FIPS认证
性能 中等 较高(针对Google场景优化) 中等
典型用户 大多数Linux发行版、Apache、Nginx Chrome、Android、gRPC Firefox、Thunderbird

这里有个常见的误解:很多人以为BoringSSL是OpenSSL的一个分支,其实不完全是。Google fork了OpenSSL之后,做了大量删减和重构,API层面有不少不兼容的地方。我遇到过最典型的问题,是一个项目在开发环境用OpenSSL编译通过,部署到Android上就链接失败——因为Android用的是BoringSSL,某些API行为不一样。

我的建议:在生成SBOM时,对于TLS库的依赖,一定要明确到“库名 + 版本号 + 编译选项”。比如“OpenSSL 1.1.1k (no-ssl3, enable-ec_nistp_64_gcc_128)”,这样后续做漏洞扫描时才能精准匹配CVE。我曾经帮一个客户做SBOM审计,发现他们只写了“OpenSSL 1.1.1”,结果实际用的是打了20多个补丁的发行版定制版本——这跟原版OpenSSL 1.1.1的漏洞情况完全不同。

至于NSS,虽然用得不如OpenSSL广泛,但在Firefox生态里是绝对的主角。NSS的API设计跟OpenSSL差异很大,如果你做的是浏览器相关的安全分析,SBOM里NSS的版本信息必须准确。我记得有一次分析Firefox的TLS实现,发现NSS对TLS 1.3 early data的处理跟OpenSSL不太一样,这直接影响了0-RTT的安全边界判断。

本章知识体系总览

下面这张图把TLS协议的核心脉络串起来了,从握手到记录协议,再到不同版本的差异和常见库的选型。做SBOM分析时,你至少得知道自己的系统站在这个图的哪个位置。

TLS协议核心知识体系 TLS协议 握手协议 记录协议 密钥协商 身份认证 密码套件协商 数据分片 加密+MAC 记录头封装 TLS 1.2 TLS 1.3 2-RTT握手 大量密码套件 RSA/ECDH密钥交换 1-RTT/0-RTT 5种AEAD套件 仅(EC)DHE OpenSSL BoringSSL NSS 其他(LibreSSL等) SBOM分析要点:明确TLS库名称 + 版本号 + 编译选项 + 启用的特性集

好了,TLS协议的基础就回顾到这里。记住,后面做SBOM分析时,你遇到的每一个TLS相关的依赖项,都能在这张图里找到它的位置。版本对不对、库选没选对、特性开没开全——这些信息,SBOM里一个都不能少。