文章目录
- 一、为什么必须单独学 TLS 1.3?
- 二、TLS 记录层与握手消息总览
- 2.1 四层封装(复习)
- 2.2 TLS 1.3 完整握手「一图流」
- 三、动画级分帧:ClientHello(帧 ①)
- 3.1 客户端发出什么?
- 3.2 关键扩展详解
- 3.3 「动画」:客户端在想什么
- 四、动画级分帧:ServerHello(帧 ②)
- 4.1 服务器选择参数
- 4.2 密钥派生(概念动画)
- 4.3 「动画」:服务器侧
- 五、动画级分帧:加密握手段(帧 ③④)
- 5.1 EncryptedExtensions
- 5.2 Certificate + CertificateVerify
- 5.3 Finished
- 5.4 握手完成状态机
- 六、TLS 1.3 套件与算法(安全视角)
- 6.1 仅 AEAD 套件
- 6.2 密钥交换组
- 七、0-RTT 与会话恢复(动画补充)
- 7.1 1-RTT 恢复(PSK)
- 7.2 0-RTT Early Data
- 7.3 Wireshark 识别
- 八、实验环境
- 8.1 拓扑
- 8.2 nginx 启用 TLS 1.3
- 8.3 确认 OpenSSL 与 curl 支持
- 九、Wireshark 抓包与解密配置
- 9.1 抓包
- 9.2 SSLKEYLOGFILE(推荐)
- 9.3 强制完整握手(避免 PSK 干扰观察)
- 十、Wireshark 逐包验证清单
- 10.1 第一包:ClientHello
- 10.2 第二包:ServerHello
- 10.3 加密握手段
- 10.4 应用数据
- 10.5 对照实验记录表(实验报告模板)
- 十一、TLS 1.2 vs 1.3:同屏对比实验
- 11.1 强制 TLS 1.2 抓一包
- 11.2 动画对比
- 十二、降级攻击与 Middlebox 兼容
- 12.1 为何 ClientHello 写 TLS 1.2?
- 12.2 降级攻击防护
- 12.3 测试服务器最低版本
- 十三、常见踩坑
- 十四、安全加固建议(运维向)
- 十五、与攻击链的关联
- 十六、完整实验步骤(90 分钟)
- 十七、本篇小结
- 附录 A:Wireshark 过滤器速查
- 附录 B:openssl 调试命令
- 附录 C:握手消息类型编号
- 附录 D:与专栏前篇关联
定位:网络基础与协议安全篇 · 把 TLS 1.3 握手机制「逐帧看懂」
声明:抓包与密钥日志实验仅限自有靶场或授权环境;勿对未授权流量解密分析。
一、为什么必须单独学 TLS 1.3?
上一篇讲了 HTTPS 抓包与解密,但TLS 1.2 与 1.3 握手差异巨大:
| 对比项 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 完整握手 RTT | 2-RTT(常见) | 1-RTT |
| 恢复会话 | Session ID / Ticket | PSK + 0-RTT |
| 密钥交换 | RSA、DHE、ECDHE 混用 | 仅 ECDHE/DHE(前向保密) |
| 证书发送时机 | ServerHello 后明文 | ServerHello 后加密 |
| 废弃算法 | 仍可能见到 | RC4、3DES、EXPORT、RSA 密钥传输等移除 |
| 中间人可见 | 更多握手明文 | 仅 ClientHello / ServerHello 主体可见 |
不懂 1.3,Wireshark 里会出现「Server Hello 之后全 Application Data」的断层,误以为是抓包坏了。本文用动画级分帧图 + 逐字段对照,在靶场里一次验证通过。
二、TLS 记录层与握手消息总览
2.1 四层封装(复习)
┌──────────────────────────────────────┐ │ HTTP / HTTP/2 应用数据 │ ├──────────────────────────────────────┤ │ TLS 记录层(Content Type + 密文) │ ← 本文重点 ├──────────────────────────────────────┤ │ TCP │ ├──────────────────────────────────────┤ │ IP │ └──────────────────────────────────────┘Content Type(记录层类型):
| 值 | 含义 | TLS 1.3 说明 |
|---|---|---|
| 22 | Handshake | ClientHello、ServerHello 等 |
| 23 | Application Data | 加密后的 HTTP |
| 20 | ChangeCipherSpec | 1.3 中通常不出现(兼容旧栈时可能有) |
| 21 | Alert | 警告/关闭 |
2.2 TLS 1.3 完整握手「一图流」
时间 → 客户端 服务器 │ │ │════════ ClientHello ══════════════════════════════►│ ① 明文 │ 版本、套件、key_share、SNI、supported_versions │ │ │ │◄════════ ServerHello ═════════════════════════════│ ② 明文 │◄──────── {EncryptedExtensions} ────────────────────│ ③ 起加密 │◄──────── {Certificate} ──────────────────────────│ │◄──────── {CertificateVerify} ────────────────────│ │◄──────── {Finished} ─────────────────────────────│ │ │ │──────── {Finished} ───────────────────────────────►│ ④ 客户端确认 │ │ │════════ Application Data (HTTP) ═════════════════►│ ⑤ 应用数据 │◄════════ Application Data ═══════════════════════│大括号{ }表示使用握手流量密钥(Handshake Traffic Secret)加密,Wireshark 配好密钥后可展开。
三、动画级分帧:ClientHello(帧 ①)
3.1 客户端发出什么?
[帧 ①] ClientHello ┌─────────────────────────────────────────┐ │ Handshake Type: Client Hello (1) │ │ Legacy Version: 0x0303 (TLS 1.2 伪装) │ ← 兼容中间盒 │ Random: 32 字节客户端随机数 │ │ Session ID: 空或 PSK 会话标识 │ │ Cipher Suites: TLS_AES_128_GCM... 等 │ │ Compression: null (0) │ │ Extensions: │ │ ├─ server_name (SNI) │ │ ├─ supported_versions (真正声明 1.3) │ │ ├─ supported_groups (x25519 等) │ │ ├─ key_share (客户端 ECDHE 公钥) │ ← 1.3 核心 │ ├─ signature_algorithms │ │ ├─ psk_key_exchange_modes (可选) │ │ └─ ... │ └─────────────────────────────────────────┘3.2 关键扩展详解
| 扩展 | 作用 | 安全意义 |
|---|---|---|
| supported_versions | 声明支持 TLS 1.3 | 真正版本协商处,Legacy Version 常为 0x0303 |
| key_share | 客户端临时 ECDHE 公钥 | 1-RTT 密钥材料,前向保密 |
| server_name (SNI) | 目标域名 | 仍明文,虚拟主机必备 |
| signature_algorithms | 签名哈希算法列表 | 验证书链用 |
| psk_key_exchange_modes | PSK 模式 | 为 0-RTT / 会话恢复铺路 |
3.3 「动画」:客户端在想什么
T+0ms 用户输入 https://lab.local T+1ms DNS 解析 → TCP SYN → SYN-ACK → ACK T+5ms 构造 ClientHello: · 我支持 TLS 1.3 · 我选 x25519,公钥是 [ClientPubKey] · 我要去的服务器名叫 lab.local T+6ms 发出 ClientHello ───────────────►四、动画级分帧:ServerHello(帧 ②)
4.1 服务器选择参数
[帧 ②] ServerHello ┌─────────────────────────────────────────┐ │ Handshake Type: Server Hello (2) │ │ Legacy Version: 0x0303 │ │ Random: 32 字节(末 8 字节有特殊含义*) │ │ Session ID: ... │ │ Cipher Suite: TLS_AES_256_GCM_SHA384 │ ← 选定套件 │ Extensions: │ │ ├─ supported_versions: TLS 1.3 (0x0304)│ │ └─ key_share: x25519 [ServerPubKey] │ ← 服务端临时公钥 └─────────────────────────────────────────┘ * 若协商 TLS 1.3,Random 最后 8 字节固定为特殊模式,便于区分降级攻击 (Wireshark 中可见 "TLS 1.3" 标注)4.2 密钥派生(概念动画)
ClientHello.key_share + ServerHello.key_share ↓ ECDH 运算 Shared Secret(共享秘密) ↓ HKDF 扩展 Handshake Traffic Secret(握手期密钥) ↓ 加密后续握手消息 {Certificate} {Finished} ... ↓ 双方 Finished 完成 Application Traffic Secret(应用期密钥) ↓ HTTP 密文双向流动前向保密(PFS):临时 ECDHE 私钥会话结束即丢弃,历史抓包无法用长期私钥解密——这也是上一篇「拿 nginx 私钥解 ECDHE 流量失败」的原因。
4.3 「动画」:服务器侧
T+20ms 收到 ClientHello T+22ms 选择 TLS_AES_256_GCM_SHA384 + x25519 T+23ms 生成 ServerPubKey,计算 Shared Secret T+24ms 派生 Handshake Traffic Secret T+25ms 发出 ServerHello ──────────────► T+26ms 同 TCP 段或紧随其后发出加密的: EncryptedExtensions + Certificate + CertificateVerify + Finished1-RTT 含义:客户端发完 ClientHello,等一轮服务器响应后,双方已可推导出应用密钥(客户端还需发 Finished,但应用数据可紧跟)。
五、动画级分帧:加密握手段(帧 ③④)
5.1 EncryptedExtensions
[帧 ③a] {EncryptedExtensions} ┌─────────────────────────────────────────┐ │ 解密后常见扩展: │ │ ├─ application_layer_protocol_negotiation (ALPN: h2 / http/1.1) │ ├─ max_fragment_length │ └─ server_name 相关确认(较少) │ └─────────────────────────────────────────┘ALPN决定走 HTTP/2 还是 HTTP/1.1,解密后 Wireshark 会显示h2或http。
5.2 Certificate + CertificateVerify
[帧 ③b] {Certificate} 服务器证书链(叶子 + 中间 CA) [帧 ③c] {CertificateVerify} 服务器用私钥对「至今所有握手 transcript」签名 证明:持有证书对应私钥的人参与了本次握手5.3 Finished
[帧 ③d] {Finished} (服务端) 对完整握手 transcript 的 MAC,证明握手未被篡改 [帧 ④] {Finished} (客户端)──► 客户端同样确认 transcript5.4 握手完成状态机
ClientHello │ ▼ ServerHello ──► 双方有 Handshake Secret │ ▼ 加密段到达 + 解密成功 │ ▼ Client Finished ──► Application Secret 就绪 │ ▼ Application Data(HTTP)六、TLS 1.3 套件与算法(安全视角)
6.1 仅 AEAD 套件
TLS 1.3 标准化套件示例:
| 套件名 | 加密 | 哈希 |
|---|---|---|
| TLS_AES_128_GCM_SHA256 | AES-128-GCM | SHA256 |
| TLS_AES_256_GCM_SHA384 | AES-256-GCM | SHA384 |
| TLS_CHACHA20_POLY1305_SHA256 | ChaCha20-Poly1305 | SHA256 |
不再有TLS_RSA_WITH_AES_...这类 RSA 密钥传输套件。
6.2 密钥交换组
| 组 | 说明 |
|---|---|
| x25519 | 最常用,速度快 |
| secp256r1 (P-256) | NIST 曲线 |
| secp384r1 | 更高强度 |
蓝队检查:禁用弱曲线、强制 TLS 1.3-only(视业务兼容性)。
七、0-RTT 与会话恢复(动画补充)
7.1 1-RTT 恢复(PSK)
老用户第二次访问:
客户端 服务器 │ │ │ ClientHello │ │ + pre_shared_key (PSK identity + binder) │ │ + key_share (可选,用于 PFS 混合模式) │ ├───────────────────────────────────────────────────►│ │◄ ServerHello + {EncryptedExtensions} │ │◄ {Finished} │ │ {Finished} ───────────────────────────────────────►│ │ Application Data ═════════════════════════════════►│Binder:证明 ClientHello 未被篡改,防 PSK 重放。
7.2 0-RTT Early Data
客户端 服务器 │ │ │ ClientHello + Early Data (HTTP GET 已加密发出) │ ├───────────────────────────────────────────────────►│ │◄ ServerHello + ... │| 优点 | 风险 |
|---|---|
| 省 1 RTT,首包更快 | Early Data无前向保密 |
| 适合 CDN、重复 GET | 重放攻击:攻击者可重放 0-RTT 请求 |
防御:服务器对 0-RTT 请求只放行幂等操作(安全策略严格时直接禁用 0-RTT)。
7.3 Wireshark 识别
tls.handshake.extensions.early_data tls.handshake.extensions.pre_shared_key八、实验环境
8.1 拓扑
受害机 / Web 192.168.56.30 nginx + TLS 1.3 分析机 Kali 或本机 Wireshark 4.x+ 测试客户端 同网段浏览器或 curl8.2 nginx 启用 TLS 1.3
# Ubuntu 22.04+ / 较新 OpenSSLsudoaptinstall-ynginx opensslsudotee/etc/nginx/snippets/tls13.conf<<'EOF' ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:TLS_AES_256_GCM_SHA384; ssl_prefer_server_ciphers off; EOF# 在 443 server 块 include snippets/tls13.confsudonginx-t&&sudosystemctl reload nginx# 验证echo|openssl s_client-connect192.168.56.30:443-tls1_32>/dev/null|grepProtocol# 应见 Protocol : TLSv1.38.3 确认 OpenSSL 与 curl 支持
openssl version# 1.1.1+ 支持 TLS 1.3curl--tlsv1.3-vkhttps://192.168.56.30/九、Wireshark 抓包与解密配置
9.1 抓包
# 在 Web 服务器或 Kali 镜像口sudowireshark-ieth1-f"host 192.168.56.30 and tcp port 443"另开终端触发握手(每次测试新开隐私窗口,避免会话恢复干扰):
curl--tlsv1.3-vkhttps://192.168.56.30/-o/dev/null9.2 SSLKEYLOGFILE(推荐)
exportSSLKEYLOGFILE=/tmp/tls13.keys.log chromium https://192.168.56.30/# 或 Chrome# Wireshark → Edit → Preferences → Protocols → TLS# (Pre)-Master-Secret log filename → /tmp/tls13.keys.log重新加载抓包文件后,加密握手段与 Application Data 应可解密。
9.3 强制完整握手(避免 PSK 干扰观察)
· 浏览器隐私模式 · curl 每次新连接:curl --tlsv1.3 --no-sessionid · 服务器侧重载 nginx 清空 ticket(靶场)十、Wireshark 逐包验证清单
10.1 第一包:ClientHello
过滤器:
tls.handshake.type == 1核对表:
| 字段 | 期望 |
|---|---|
| Handshake Type | Client Hello (1) |
| Version (Legacy) | 0x0303 |
| Extension: supported_versions | 包含 TLS 1.3 (0x0304) |
| Extension: key_share | 含 x25519 与公钥长度 32 字节 |
| Extension: server_name | 你的域名或 IP |
| Cipher Suites | 含 TLS_AES_128_GCM_SHA256 等 |
展开路径示例:
Transport Layer Security └─ TLSv1.3 Record Layer: Handshake Protocol: Client Hello └─ Extension: supported_versions (TLS 1.3) └─ Extension: key_share (x25519)10.2 第二包:ServerHello
tls.handshake.type == 2| 字段 | 期望 |
|---|---|
| supported_versions | TLS 1.3选中 |
| Cipher Suite | TLS_AES_256_GCM_SHA384 等 |
| key_share | 服务端 x25519 公钥 |
10.3 加密握手段
解密成功后,同一 TCP 流中应看到:
Handshake Protocol: Encrypted Extensions Handshake Protocol: Certificate Handshake Protocol: Certificate Verify Handshake Protocol: Finished过滤器(解密后):
tls.handshake.type == 8 # Encrypted Extensions tls.handshake.type == 11 # Certificate tls.handshake.type == 15 # Certificate Verify tls.handshake.type == 20 # Finished10.4 应用数据
http || http2或:
tls.record.content_type == 23Follow → HTTP/2 Stream 或 HTTP Stream,看到GET /即全链路打通。
10.5 对照实验记录表(实验报告模板)
| 序号 | 消息 | 明文/加密 | 关键字段 | 截图编号 |
|---|---|---|---|---|
| 1 | ClientHello | 明文 | supported_versions, key_share, SNI | fig-01 |
| 2 | ServerHello | 明文 | 选定套件、Server key_share | fig-02 |
| 3 | EncryptedExtensions | 加密 | ALPN = h2/http1.1 | fig-03 |
| 4 | Certificate | 加密 | 颁发者、SAN | fig-04 |
| 5 | Finished ×2 | 加密 | 握手完成 | fig-05 |
| 6 | Application Data | 加密→解密 | GET 请求 | fig-06 |
十一、TLS 1.2 vs 1.3:同屏对比实验
11.1 强制 TLS 1.2 抓一包
curl--tlsv1.2-vkhttps://192.168.56.30/-o/dev/null| 现象 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| ServerHello 后 | 常有Certificate 明文 | Certificate在加密块内 |
| 密钥交换 | ServerKeyExchange / ClientKeyExchange 可能单独出现 | 合并进 key_share |
| 报文数量 | 通常更多 | 更少、更紧凑 |
| ChangeCipherSpec | 常见 | 通常无 |
过滤器对比:
tls.handshake.type == 11 && frame.number < 501.2 往往在早期帧即可见 Certificate;1.3 需解密后才见。
11.2 动画对比
TLS 1.2(简化): C: ClientHello ──────► S: ServerHello ───────► S: Certificate ───────► 明文 S: ServerHelloDone ───► C: ClientKeyExchange ─► C: ChangeCipherSpec ──► C: Finished ──────────► S: ChangeCipherSpec ──► S: Finished ──────────► ══ Application Data ══ TLS 1.3: C: ClientHello ──────► S: ServerHello + {Cert+Finished...} ──► C: {Finished} ───────► ══ Application Data ══十二、降级攻击与 Middlebox 兼容
12.1 为何 ClientHello 写 TLS 1.2?
部分老旧防火墙见到「非 0x0303」就丢包。客户端在Legacy Record Layer写 0x0303,在supported_versions扩展里声明 1.3。
12.2 降级攻击防护
TLS 1.3 在 ServerHello Random 嵌入特殊标记;Finished 覆盖完整 transcript。
蓝队:禁用服务器 TLS 1.0/1.1/1.2,可减攻击面(需评估业务)。
12.3 测试服务器最低版本
# 应失败(若仅允许 1.2+)openssl s_client-connect192.168.56.30:443-tls1# 应成功openssl s_client-connect192.168.56.30:443-tls1_3十三、常见踩坑
| 问题 | 原因 | 解决 |
|---|---|---|
| 看不到 TLS 1.3 | OpenSSL/nginx 过旧 | 升级;检查ssl_protocols |
| ServerHello 后「断层」 | 1.3 握手加密 | 配置 SSLKEYLOGFILE |
| 一直是 Session Ticket 恢复 | 浏览器缓存 PSK | 隐私窗口 /--no-sessionid |
| Wireshark 显示 TLS 1.2 | 仅看 Legacy Version | 看supported_versions扩展 |
| 解密后无 Certificate | 解密失败或走 1.2 | 检查 key log 路径、重抓 |
| curl 无 1.3 | curl/openssl 旧 | curl -V检查 |
| ALPN 协商 h2 但看不懂 | HTTP/2 二进制分帧 | 用 Follow HTTP/2 Stream |
十四、安全加固建议(运维向)
□ ssl_protocols 至少 TLSv1.2 TLSv1.3,优先淘汰 1.2 □ 证书 ECDSA P-256 或 RSA 2048+,自动续期 □ 启用 OCSP Stapling □ 评估 0-RTT:对外 API 建议 off □ 配置 HSTS(见 HTTPS 篇) □ 定期 sslscan / testssl.sh 扫描弱套件testssl.sh 快测:
gitclone https://github.com/drwetter/testssl.sh.git ./testssl.sh--protocols192.168.56.30:443十五、与攻击链的关联
TLS 1.3 普及 → 纯「拿服务器 RSA 私钥解流量」失效 → MITM 更依赖「用户信任恶意 CA」或 SSL 剥离 → 0-RTT 重放成为新审计点 → 元数据(SNI、JA3)仍是监控入口下一篇SSL 剥离 将解释:即使有 HTTPS,为何仍可能「翻车」。
十六、完整实验步骤(90 分钟)
□ 1. nginx 开启 TLS 1.3,openssl s_client 验证 □ 2. 配置 SSLKEYLOGFILE + Wireshark TLS 密钥路径 □ 3. 隐私窗口访问 https://192.168.56.30,保存 full.pcapng □ 4. 定位 ClientHello,截图 supported_versions + key_share □ 5. 定位 ServerHello,确认选中 TLS 1.3 与套件 □ 6. 解密后展开 Certificate、ALPN □ 7. Follow HTTP/2 或 HTTP,确认应用层 □ 8. 再抓一包 curl --tlsv1.2,对比 Certificate 是否早期明文 □ 9. (可选)抓 0-RTT:同站第二次访问,查 early_data 扩展 □ 10. 填写「对照实验记录表」,写 200 字握手总结十七、本篇小结
┌─────────────────────────────────────────────────────────────┐ │ TLS 1.3 核心记忆 │ ├─────────────────────────────────────────────────────────────┤ │ 1-RTT:ClientHello → ServerHello + 加密块 → Client Finished │ │ 版本在 supported_versions;key_share 完成 ECDHE │ │ Certificate 在握手加密段内,无密钥则 Wireshark「断层」 │ │ SSLKEYLOGFILE 是本地学习解密的最佳合法手段 │ │ 0-RTT 快但有重放风险;PFS 使历史密文难用长期私钥破解 │ └─────────────────────────────────────────────────────────────┘下一篇预告:《一次真实的 SSL 剥离攻击:为什么 HTTPS 也会翻车》——HSTS、sslstrip、透明代理的组合拳。
附录 A:Wireshark 过滤器速查
tls tls.handshake.type == 1 tls.handshake.type == 2 tls.handshake.extensions_server_name tls.handshake.extensions.supported_versions tls.handshake.extensions_key_share tls.handshake.type == 8 ssl.handshake.type == 11 http2附录 B:openssl 调试命令
# TLS 1.3 握手详情openssl s_client-connect192.168.56.30:443-tls1_3-msg# 查看套件openssl ciphers-v|grepTLS_AES# 会话复用测试openssl s_client-connect192.168.56.30:443-tls1_3-reconnect附录 C:握手消息类型编号
| Type | 名称 | TLS 1.3 常见 |
|---|---|---|
| 1 | ClientHello | ✓ |
| 2 | ServerHello | ✓ |
| 8 | EncryptedExtensions | ✓ |
| 11 | Certificate | ✓ |
| 15 | CertificateVerify | ✓ |
| 20 | Finished | ✓ |
附录 D:与专栏前篇关联
| 前篇 | 关联 |
|---|---|
| HTTP/HTTPS | 解密 Application Data 的前提 |
| Wireshark | 显示过滤器 + TLS 密钥配置 |
| ARP MITM | 握手可见,内容仍加密;剥离攻击在应用层 |