0Pricing
Cryptology Academy · 课时

应用层代码中的时序攻击

学习字符串比较的耗时如何泄露秘密,以及恒定时间比较如何防止此类泄露。

应用层代码中的时序攻击 是 CoddyKit 上的免费 Cryptology Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Cryptology Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Cryptology Academy 课程共包含 4 节课。

非常数时间字符串比较

大多数编程语言中的标准字符串相等运算符会在发现不匹配后立即终止。Python 对字节对象使用 == 时、C 的 strcmp 以及 Java 的 String.equals 都会在找到第一个不同的字节后立即返回。对于普通字符串比较,这是一个优化;但对于比较 MAC 标签或密码等秘密值,它会产生可测量的时间侧信道,从而泄露信息。

测量 HMAC 比较时间

攻击者测量将提交的 HMAC 标签与正确值进行比较所花费的时间。提交首字节正确匹配的 HMAC,比首字节错误的 HMAC 所需时间略长(多进行一次字节比较)。通过为第一个字节分别提交每种可能的值并测量响应时间,攻击者可以确定正确的首字节。该过程逐字节重复,直到恢复完整标签。

攻击所需的实际计时精度

现代网络计时攻击可以在互联网范围内解析几十到几百纳秒的时间差。对于 32 字节的 HMAC 比较,如果每个正确字节会增加约 10-100ns 的处理时间,那么通过足够多次重复测量来平均掉网络抖动,就能得到可测量的信号。在本地网络环境中,只要进行足够的统计采样,甚至单纳秒级的差异也可以被利用。

Python == 运算符的漏洞

在 Python 中,使用 == 比较 MAC 标签并不安全:如果 mac == submitted_mac,会根据第一次不匹配的位置返回 True 或 False。攻击者提交数千个精心构造的标签并测量响应时间,就可以逐字节恢复预期标签。这一漏洞曾出现在生产环境的网络应用中:这些应用错误地实现了会话令牌或 API 密钥比较,未使用常数时间函数。

Python 中的 hmac.compare_digest

Python 的 hmac.compare_digest(a, b) 会以常数时间比较两个字节值或字符串值,无论第一次不匹配出现在哪里,耗时都相同。该函数使用 C 实现,以确保即使受到 Python 字节码解释开销影响,也能保持常数时间行为。比较 MAC 标签、会话令牌、API 密钥,或任何时间信息可能造成危险的值时,始终使用 hmac.compare_digest。

OpenSSL 中的 CRYPTO_memcmp

OpenSSL 提供 CRYPTO_memcmp(a, b, length) 用于进行常数时间内存比较。与 memcmp 不同,它无论是否存在提前出现的不匹配,都会处理全部 length 个字节。返回值相等时为零,不同时为非零。务必始终比较完整的预期长度:按较短长度比较不同长度的值,仍可能泄露长度信息。在使用 OpenSSL 的 C/C++ 代码中进行任何安全关键比较时,都应使用 CRYPTO_memcmp。

RSA 上的计时攻击:Bleichenbacher

计时攻击不止针对字符串比较。Bleichenbacher 于 2006 年针对 RSA PKCS#1 v1.5 解密发起的攻击,证明了针对 SSL/TLS 实现的实用计时预言机攻击。RSA 私钥操作的耗时会根据解密值是否具有有效 PKCS#1 填充而变化。攻击者提交数千个精心构造的密文后,就可能恢复 RSA 私钥。这推动了 RSA-OAEP 和常数时间 RSA 实现的发展。

AES 的缓存计时攻击

使用查找表的 AES 实现(常见于追求性能的场景)会根据密钥和明文访问不同的表项。缓存命中与缓存未命中会产生可测量的时间差,从而泄露访问了哪些表项的信息。该侧信道可能泄露 AES 密钥。防御方法是使用不依赖表查找的 AES 实现,例如使用 AES-NI 硬件指令,或使用按位切片的软件实现。

常数时间实现原则

编写常数时间代码需要避免以下情况:依赖秘密数据的条件分支(使用带掩码的无分支选择)、取决于秘密数据的内存访问模式(避免使用秘密值作为索引的查找表),以及延迟依赖秘密值的任何操作(例如,某些处理器上的除法)。编译器可能会优化掉常数时间结构,因此关键代码段可能需要使用汇编或 volatile 内存访问。

AEAD 消除了应用层 MAC 比较

防御 MAC 比较计时攻击的最佳方法,是使用 AEAD 模式(GCM、ChaCha20-Poly1305),并将 MAC 验证交给密码学库。库的实现会在内部执行常数时间验证。如果您正确使用 AEAD(任何篡改都会导致解密失败,且绝不在验证标签之前解密),应用代码就无需比较 MAC 标签,从而彻底消除计时漏洞。

测试计时漏洞

检测计时漏洞需要对响应时间分布进行统计分析。tlsfuzzer、计时攻击测试脚本和 dudect 框架等工具有助于检测密码学实现中的时间差异。对本应产生相同耗时的输入的响应时间样本进行 t 检验,可以揭示具有统计显著性的差异。可能出现假阴性;除了测试之外,对常数时间代码进行审查同样必不可少。

常数时间比较

为防止计时攻击,应该使用哪个 Python 函数来安全地比较 HMAC 标签?

计时攻击回顾

计时攻击回顾:提前退出的字符串比较会通过响应时间差异逐字节泄露秘密值;只要有足够样本,就能在网络上测量这种差异;在 Python 中使用 hmac.compare_digest,在 OpenSSL 中使用 CRYPTO_memcmp 进行常数时间比较;RSA 填充计时攻击会危及私钥(使用常数时间 RSA 和 OAEP);AES 查找表计时攻击会泄露密钥位(使用 AES-NI 或按位切片实现);而 AEAD 库验证则无需在应用层比较 MAC。

常见问题解答

「应用层代码中的时序攻击」课时是免费的吗?

是的 — 「应用层代码中的时序攻击」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Cryptology Academy 课程的其余内容,请升级到 CoddyKit PRO。 Cryptology Academy 课程共包含 4 节课。

「应用层代码中的时序攻击」这节课中我会学到什么?

学习字符串比较的耗时如何泄露秘密,以及恒定时间比较如何防止此类泄露。 你通过在浏览器中直接运行的动手代码来练习 Cryptology Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Cryptology Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Cryptology Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「应用层代码中的时序攻击」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Cryptology Academy 课中编写并运行代码吗?

能。每节 Cryptology Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 深入了解填充预言机攻击
  2. 重放攻击与随机数重复使用漏洞
  3. 应用层代码中的时序攻击
  4. 常见密码学误用模式
← 返回 Cryptology Academy