Lucky 13 — 用毫秒级时间差把 TLS 加密看穿
待复核Lucky 13 是一个TLS 协议的攻击:攻击者不破密钥、不挖算法漏洞,只反复发包,从服务器响应快了几微秒还是慢了几微秒里,一个字节一个字节把别人 HTTPS 流量里的密码、cookie 还原出来。
日常类比:你不知道保险柜密码,但每按一个数字,柜门会”咔哒”一下。错的数字咔哒慢,对的数字咔哒快——你只要安静地按几千次、记下时间,密码就出来了。
这个攻击的”幸运 13”指的是:TLS 在算消息认证码(MAC)时会先吞掉 13 字节的协议头。这个 13 字节是攻击者下手的支点。
不理解 Lucky 13,下面这些事都没法解释:
- 为什么 TLS 1.3(2018)直接删掉了用了 20 年的 CBC 加密模式
- 为什么现在浏览器里 HTTPS 几乎都是 AES-GCM 或 ChaCha20-Poly1305(这类叫 AEAD)
- 为什么”理论上安全”的密码协议会被一个时间测量打穿——工程实现的小细节能毁掉数学证明
- 为什么一系列以”动物”命名的攻击(BEAST / CRIME / Lucky 13 / POODLE / Heartbleed)能在 2011-2014 把 TLS 整个推倒重来
Lucky 13 攻击的关键是 三个事实拼到一起:
-
TLS 1.0–1.2 的 CBC 套件用 MAC-then-Encrypt:先算消息认证码、把 MAC 拼到明文后面、再整体用 CBC 加密。解密时反过来:先 CBC 解密、再按末尾字节剥 padding、再对「剩下的明文」跑 HMAC。
-
剥完 padding 后,送进 HMAC 的字节数会变:攻击者改密文末块,服务器会解出不同的「padding 长度」,于是 HMAC 吃到的数据长短不同。即便实现已经把「报错路径」做成一样长,HMAC 本身算多久仍会漏馅——这才是 Lucky 13,不是早期那种「padding 错就立刻返回」的简单 padding oracle。
-
HMAC(如 SHA-1)按 64 字节分块:数据长度刚跨过块边界时,多跑一次压缩函数,多出几微秒。攻击者把长度卡在边界两侧,反复测响应时间,反推末字节是几;逐字节推进——大约两小时可还原一个 cookie。
13 这个数字来自 TLS 协议头:5 字节记录头 + 8 字节序列号 = 13 字节。HMAC 一开始就要把这 13 字节吞进去,吞完正好让攻击的时间差对齐到容易测的位置——攻击者很”幸运”。
案例 1:浏览器里看不见的”被淘汰”
Section titled “案例 1:浏览器里看不见的”被淘汰””打开 Chrome,访问任何 HTTPS 网站,按 F12 → Security 面板。你会看到类似:
Connection - secure connection settingsTLS 1.3, X25519, AES_256_GCM注意 AES_256_GCM——这是 AEAD 模式,没有 MAC-then-Encrypt 这一步,加密和认证一起做。
2013 年之前你看到的多半是 AES_128_CBC_SHA——CBC 模式 + 单独 MAC。Lucky 13 直接打这种组合。
可以用 OpenSSL 看支持的 cipher:
openssl ciphers -v 'HIGH' | grep CBC如果输出里还有 CBC 套件,那台服务器还有”被攻击的能力”。现代 Nginx / Caddy 默认配置已经把 CBC 移到列表末尾或彻底关掉。
案例 2:Lucky 13 怎么”试”出一个字节
Section titled “案例 2:Lucky 13 怎么”试”出一个字节”攻击者想知道某个 CBC 块 C 解密后的最后一字节 b。流程:
- 构造一条伪造记录,把
C放在末块,发给服务器 - 服务器 CBC 解密 → 按末字节解释 padding 长度 → 对剩余字节跑 HMAC → 报错(MAC 几乎总会错)
- 测整段响应时间
t(差在 HMAC 多跑了几次压缩) - 改密文里影响「解密后末字节」的那一字节,让服务器解出另一种 padding 长度
- 对 256 种候选各测很多次,看哪一种让 HMAC 输入长度跨过 64 字节边界、时间最「特别」——那就是
b
每个字节要发几千到几百万次包,才能把几微秒的差从网络抖动里捞出来。
案例 3:为什么 TLS 1.3 删 CBC
Section titled “案例 3:为什么 TLS 1.3 删 CBC”TLS 1.3(RFC 8446,2018)的设计直接砍掉 CBC、RC4、3DES,只留 AEAD:
AES_128_GCM_SHA256AES_256_GCM_SHA384CHACHA20_POLY1305_SHA256
AEAD 把”加密 + 认证”绑成一个原子操作。Lucky 13 类的”先解密看 padding,再验 MAC”漏洞结构上不存在了。
-
“理论安全”不等于”实现安全”:CBC + HMAC 在密码学论文里有可证安全证明,但前提是”两步操作时间不可观测”。Lucky 13 证明这前提在网络上做不到。
-
修补不是真修:OpenSSL 当时打了补丁,让 padding 错误和 MAC 错误的代码路径走一样长的时间。但补丁本身又被发现有侧信道——常时间编程极难写对。
-
MAC-then-Encrypt 的设计天生危险:先认证再加密(Encrypt-then-MAC)顺序就没这问题,因为收方先验 MAC、错就直接拒,根本不会进解密路径。TLS 选错了顺序,付了 20 年代价。
-
DTLS 比 TLS 还危险:DTLS(基于 UDP 的 TLS)允许重传,攻击者可以无限次试同一个密文,效率比 TLS 高一个数量级。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用攻击的目标(2013 年时):
- TLS 1.0 / 1.1 / 1.2 用 CBC 模式的连接
- DTLS 1.0 / 1.2
- OpenSSL / NSS / GnuTLS / PolarSSL / BouncyCastle 等所有主流实现
不适用:
- TLS 1.3(结构上无 CBC,已免疫)
- AEAD 模式(AES-GCM / ChaCha20-Poly1305)
- 启用 Encrypt-then-MAC 扩展(RFC 7366)的连接
- 网络抖动 > 时间差的场景(攻击需要稳定测量微秒级)
案例 4:常时间编程到底有多难
Section titled “案例 4:常时间编程到底有多难”OpenSSL 给 Lucky 13 打的补丁,思路是:不管 padding 对不对,都跑同样多的 HMAC 压缩。看起来简单,实现却踩了一堆坑:
// 简化示意:错的写法if (padding_ok) { hmac(data, real_len);} else { hmac(data, fake_len); // 时间不一样}
// 对的写法(伪代码)hmac_blocks = compute_blocks(max_possible_len);for i in 0..hmac_blocks { sha1_compress(...); // 永远跑 max 次}但这还不够:memcpy 的长度、CPU 分支预测、内存访问模式都可能泄露。真正常时间的 HMAC 一直到 2014 年才被 OpenSSL 写对。
历史小故事(可跳过)
Section titled “历史小故事(可跳过)”- 2011 BEAST(Duong-Rizzo):CBC 的 IV 可预测漏洞,把 CBC 第一刀
- 2012 CRIME:TLS 压缩侧信道
- 2013 Lucky 13(本文):CBC padding 时间侧信道
- 2014 POODLE(Möller 等):SSL 3.0 的 padding oracle,逼业界彻底淘汰 SSL 3.0
- 2014 Heartbleed(OpenSSL 内存泄露):和 Lucky 13 不同类,但同年放大了”TLS 不安全”的恐慌
- 2018 TLS 1.3 发布:清理积累 20 年的设计债
Lucky 13 是这条链子的中间一环——单独看不致命,叠加起来推倒了整个旧 TLS。
- 侧信道是真威胁,不是论文玩具——几微秒的时间差,跨网络也能稳定测出来
- 协议设计的顺序至关重要:MAC-then-Encrypt vs Encrypt-then-MAC,一字之差差 20 年
- 常时间编程极其难:编译器、CPU 缓存、分支预测都会让”看起来对称的代码”实际不对称
- AEAD 是工程上的胜利:把”加密 + 认证”绑成一个原子操作,少一个组合就少一类漏洞
- 协议演化靠攻击驱动:BEAST → CRIME → Lucky 13 → POODLE 这条链推动了 TLS 1.3 的彻底重构
- 论文 PDF:Lucky Thirteen: Breaking the TLS and DTLS Record Protocols(作者主页,含演示视频)
- TLS 1.3 RFC:RFC 8446(看 §1.2 “Major Differences from TLS 1.2”)
- 通俗讲解:Matthew Green 博客 — Attack of the week: Lucky 13
- aes —— Lucky 13 攻击的对象不是 AES 本身,而是它外面那层 CBC 模式
- tls-1.3 —— TLS 1.3 的设计动机一半就是把 Lucky 13 这类攻击堵死
- aes —— AES 在 CBC 模式里被使用,Lucky 13 不破 AES,破”AES + HMAC + 顺序”的组合
- tls-1.3 —— TLS 1.3 删掉所有 CBC 模式,结构上免疫 Lucky 13
- heartbleed-2014 —— 同期 TLS 攻击,但走的是内存读越界路线,不同类
- libsignal —— 现代端到端加密直接用 AEAD,吸取了 Lucky 13 的教训
- amplification-hell-2014 —— Amplification Hell 2014 — 把家用宽带放大成几百 Gbps 的反射攻击
- logjam-2015 —— Logjam 2015 — 全世界共用一把锁,国家级窃听者一次撬完