首页/我的作品/密码学/13.2 常见陷阱:随机数、侧信道攻击、时间攻击
密码学:守护数字世界的秘密

13.2 常见陷阱:随机数、侧信道攻击、时间攻击

第 71 / 78 篇 · Crazy Boy

即使你使用了标准的加密算法和成熟的库,仍然可能在工程实现中踩进坑里。这些陷阱往往隐藏在代码的细节中,却能让整个安全防线瞬间崩塌。本节我们就来剖析三个最致命的陷阱:随机数、侧信道攻击和时间攻击。

陷阱一:不安全的随机数

随机数在密码学中无处不在:生成密钥、初始化向量(IV)、盐值、随机数挑战……一旦随机数可预测,整个加密体系就失去了根基。

问题

许多开发者错误地使用通用编程语言提供的“随机数”函数,如 C 的 rand()、Java 的 java.util.Random、Python 的 random 模块。这些函数是伪随机数生成器,其种子通常基于时间戳,生成序列可被预测。攻击者一旦知道种子,就能重现所有“随机”值。

案例

2012年,某大型比特币钱包服务因使用 rand() 生成私钥,导致大量用户的私钥被穷举,数百万美元被盗。2016年,一家知名物联网厂商使用固定密钥加密设备通信,原因是其“随机数”生成器在每次启动时产生相同序列。

正确做法

使用密码学安全的随机数生成器(CSPRNG)。这类生成器从操作系统或硬件熵源获取真随机数,保证不可预测。

  • Linux/Unix:/dev/urandom
  • Windows:CryptGenRandom
  • 各语言:Python 的 secrets 模块,Java 的 java.security.SecureRandom,Go 的 crypto/rand,Node.js 的 crypto.randomBytes()。
# 错误示范
import random
key = random.getrandbits(128) # 可预测!

# 正确示范
import secrets
key = secrets.token_bytes(16) # 密码学安全

陷阱二:侧信道攻击

加密算法在数学上是安全的,但在物理世界里,它的运行会泄漏信息。侧信道攻击就是利用这些物理泄漏——功耗、电磁辐射、运行时间、声音甚至缓存行为——来推断密钥。

问题

当程序处理密钥时,CPU 的功耗会随运算数据变化。攻击者用高精度仪器测量芯片的功耗波形,就能反推出密钥的每一位。另一种常见侧信道是缓存时间攻击,通过测量访问内存的时间差异,推测出秘密数据在缓存中的位置。

案例

2017年,Meltdown和Spectre漏洞曝光,它们利用现代CPU的推测执行和缓存侧信道,从内核内存中读取任意数据。虽然主要影响操作系统,但也证明了侧信道攻击的威力。更早的RSA时序攻击表明,通过测量解密不同输入的时间,可以逐步恢复私钥。

正确做法

  • 使用常数时间实现:所有操作(包括条件分支)的执行时间与数据无关。高级密码库(如 libsodium)的底层实现已经考虑了这一点。
  • 禁用不必要的优化:某些编译器优化可能引入时序变化。
  • 使用硬件加速:利用 CPU 的 AES-NI 指令集,其执行时间与数据无关。
  • 避免自己实现:直接调用经过审计的库,不要自己写条件分支处理密钥。

陷阱三:时间攻击

时间攻击是侧信道攻击的一种特例,专注于测量不同输入下算法运行时间的差异。例如,在字符串比较中,如果函数一遇到不同字符就立即返回,那么比较两个字符串的时间就会随“匹配前缀长度”而变化。攻击者可以利用这个时间差,一步步猜出正确的字符串。

问题

很多开发者习惯用 == 或 strcmp 比较密码、签名、哈希值。这些函数通常会在第一个不匹配的字节处提前返回,导致运行时间与匹配长度相关。攻击者通过测量大量请求的时间,可以逐字节还原出正确的值。

案例

2010年,研究人员发现 OpenSSL 的 HMAC 验证存在时间漏洞,攻击者可以伪造签名。2018年,某知名身份验证服务因使用普通字符串比较,导致攻击者可以枚举出重置密码的 token。

正确做法

使用常数时间比较函数,它总是检查所有字节才返回结果。所有主流密码库都提供此类函数。

  • Python:hmac.compare_digest(a, b)
  • Java:MessageDigest.isEqual(a, b)
  • Go:subtle.ConstantTimeCompare(a, b)
  • Node.js:crypto.timingSafeEqual(a, b)
# 错误示范
if user_input == stored_hash: # 时间可被测量
grant_access()

# 正确示范
import hmac
if hmac.compare_digest(user_input, stored_hash):
grant_access()

总结

随机数、侧信道、时间攻击是密码工程中最常见、最隐蔽的陷阱。它们利用了现实世界与理想数学之间的缝隙。避开这些陷阱的方法并不复杂:使用正确的工具,遵循安全实践,并永远不要自己实现底层操作。下一节,我们将学习如何正确选择和使用密码学库——这是将理论转化为安全实践的关键一步。