3. TLS记录层协议精解

TLS 握手协商出的密钥与算法最终都要落到记录层(Record Layer)来执行。记录层是 TLS 的“运输封装层”:上层握手消息、告警、应用数据都被切成记录(record),逐条加密、加完整性校验,再交给 TCP 传输。理解记录层,等于理解了 TLS 在网络上真正“长什么样”。

记录层的封装结构

每条 TLS 记录由 5 字节头部和载荷组成。头部三个字段:ContentType(1 字节)、legacy_record_version(2 字节)、length(2 字节)。载荷长度上限为 2^14 = 16384 字节,这是 TLS 明文分片的上限。

ContentType 常见取值:20 表示 ChangeCipherSpec,21 表示 Alert,22 表示 Handshake,23 表示 Application Data。在 TLS 1.3 中,握手消息本身也走 23(Application Data)类型,靠内层握手头区分,这是与 TLS 1.2 的重要差异。版本字段在 TLS 1.3 中固定写 0x0303(TLS 1.2),仅为兼容中间设备,真实版本由 supported_versions 扩展协商。

分片、压缩与加密流程

发送方向的处理顺序是:分片 → 压缩(历史)→ 加 MAC → 加密。上层数据先被切成不超过 16384 字节的片段,每片独立成一条记录。压缩在 TLS 1.3 中已被彻底移除,TLS 1.2 虽保留但默认禁用,原因是 CRIME 等攻击利用压缩率泄露明文信息,因此现代实现一律不启用。

完整性保护有两种模式。TLS 1.2 的 AEAD 套件(如 AES-GCM)使用“先加密后认证”的 AEAD 结构,MAC 由 AEAD 内部完成;而 CBC 类套件使用 MAC-then-Encrypt,历史上易受 Lucky13 等时序攻击。TLS 1.3 只保留 AEAD,彻底删除了 CBC 与流密码,结构更干净。

序列号与防重放

每条记录都有一个 64 位序列号,从 0 开始,每发一条记录加 1,收发双方各自维护独立计数器。序列号不随记录传输,而是作为 AEAD 的 nonce 输入参与计算。接收方用自己维护的期望序列号解密,一旦不匹配就丢弃并告警。这样攻击者无法重放旧记录,因为重放记录的序列号与接收方期望值不符。序列号在密钥更新(KeyUpdate)后重置,配合新密钥使用。

TLS 记录层封装与解密流程 发送侧 上层数据 分片 ≤16384B 压缩已废弃 AEAD 加密 nonce=序列号 加 5B 记录头 类型/版本/长度 TLS 记录结构 类型 1B 版本 2B 长度 2B 加密载荷 +AEAD tag ContentType: 20/21/22/23 TLS1.3 版本字段固定 0x0303 序列号不传输,仅参与 nonce 重放记录序列号不匹配 → 丢弃 KeyUpdate 后序列号重置 接收侧 读取 5B 头 校验长度/类型 序列号比对 防重放检查 AEAD 解密 验证 tag 交付上层 握手/应用数据 TCP TCP Wireshark 观察要点 • 未配置密钥时:仅见 TLS 记录头,载荷显示为 Encrypted Application Data • 配置 SSLKEYLOGFILE 后:Preferences → Protocols → TLS 指定密钥日志文件,可解密查看明文 • 关注字段:Content Type、Length、Sequence Number(解密后可见)、Record Version

用 Wireshark 观察记录层

抓包时若没有密钥,Wireshark 只能看到记录头,载荷显示为 “Encrypted Application Data”。要解密,需要客户端导出会话密钥。以浏览器为例,设置环境变量 SSLKEYLOGFILE 指向一个可写文件,浏览器会把每条连接的密钥材料追加写入。然后在 Wireshark 的 Preferences → Protocols → TLS 中指定该文件路径,即可自动解密。

# Linux/macOS 启动浏览器前设置
export SSLKEYLOGFILE=$HOME/tls_keys.log
# 验证文件生成
ls -l $HOME/tls_keys.log
# 文件内容形如:
# CLIENT_RANDOM <32字节hex> <主密钥hex>

解密后,在 Wireshark 过滤栏输入 tls.record.content_type == 23 可只看应用数据记录;输入 tls.record.length > 16000 可观察接近上限的大记录。展开一条记录,能看到 Content Type、Length、以及解密后的 TLS 内层结构。若看到多条 16384 字节记录连续出现,说明上层数据被分片传输,这是记录层分片的直接证据。

SSLKEYLOGFILE 只应用于自己的测试环境。它导出的密钥可解密全部会话,泄露后等同于泄露通信内容,生产环境严禁开启或分发该文件。

关键判断与常见误区

第一,序列号不传输,所以抓包看不到它,但解密后 Wireshark 会显示推导出的序列号。第二,TLS 1.3 的握手记录外层类型是 23,不要因为看到 Application Data 就以为握手已结束。第三,记录长度字段是密文长度,包含 AEAD tag(通常 16 字节),因此实际明文比 length 小 16 字节。第四,压缩字段虽在 TLS 1.2 规范中存在,但现代栈一律协商 null 压缩,看到非零压缩方法应视为异常。

掌握记录层后,后续分析证书、握手扩展、会话恢复时,都能准确判断“这条记录属于哪个阶段、是否被正确保护、序列号是否连续”,这是 TLS 排障与安全审计的基本功。