实现与安全实践

时序攻击与常数时间实现

问题

如果密码操作的执行时间依赖于秘密数据(密钥、明文),攻击者可以通过精确测量时间来推断这些数据。

典型场景

  • 字符串比较:很多实现逐字节比较 MAC/HMAC 值,遇到第一个不匹配就返回。攻击者可以逐字节猜测,每次调整猜测值,观察响应时间变化。
  • RSA 中的 Montgomery 约简:条件分支可能泄露密钥比特。
  • AES 查表:S 盒查表操作可能因 CPU 缓存命中/未命中而产生时间差异。

防御

  • 常数时间比较:比较两个值时遍历所有字节,用位操作累积结果。如:result |= a[i] ^ b[i],最后检查 result == 0。
  • 避免秘密相关的分支:if (secret_bit) 语句是危险的,用位掩码替代。
  • 避免秘密相关的内存访问:不要用秘密值作为数组索引。

白盒密码学

在白盒攻击环境中,攻击者完全控制执行环境:可以观察和修改内存、设置断点、反汇编代码。这在 DRM(数字版权管理)、移动支付等场景中很常见。

白盒密码的目标:即使攻击者能观察每一步运算,也无法提取出密钥。

基本技术:

  • 编码/混淆:将密钥融入查找表和网络变换中,使得密钥不以明文形式出现
  • 随机化:在计算中注入随机性,使每次执行看起来不同

目前白盒密码的实用方案都已被攻破。它是密码学中一个活跃但困难的领域。

密码学库

OpenSSL

最广泛使用的密码学库,C 语言编写。提供 SSL/TLS 协议实现和大量密码算法。优点是全面、经过长期审查,缺点是 API 复杂、历史包袱重、容易误用。

Libsodium

基于 NaCl(Daniel J. Bernstein 设计)的现代密码学库。设计理念:安全的默认值、简洁的 API、难以误用。推荐用于新项目。

其他重要库

  • BoringSSL:Google 维护的 OpenSSL 分支,去除了大量遗留代码
  • Bouncy Castle:Java/C# 平台的密码学库
  • cryptography:Python 的高层密码学库(基于 OpenSSL)

常见实现错误

  1. 自己实现密码算法:几乎总是不安全的。"Don't roll your own crypto"
  2. 使用 ECB 模式:暴露数据模式
  3. IV/Nonce 重复:特别是 GCM 模式中,Nonce 重复会导致密钥流重用,灾难性后果
  4. 使用 MD5/SHA-1:用于安全目的
  5. 硬编码密钥:密钥应存储在安全的密钥管理系统中
  6. 不做完整性检查:只加密不认证(缺少 MAC/AEAD)
  7. 不验证证书:TLS 连接中跳过证书验证等于没有加密