Hotdry.

Article

澄清 /dev/urandom 熵耗尽迷思:何时该用阻塞式随机数生成器

拆解 Linux 随机数生成器的常见误解,厘清 /dev/urandom 与 /dev/random 的技术本质差异,提供加密应用中的可落地选择策略。

2026-05-14security

在 Linux 系统编程中,一个流传甚广的「安全准则」是:"/dev/urandom 不够安全,加密场景必须用 /dev/random"。这个建议甚至出现在许多技术文档和安全指南中。然而,这实际上是一个基于误解的迷思。理解 Linux 内核随机数生成器(RNG)的真实工作机制,对于构建正确的加密系统至关重要。

核心误解:「真随机」与「伪随机」的二元对立

许多人认为 /dev/random 输出「真正的随机数」,而 /dev/urandom 只是「伪随机数生成器(PRNG)」,因此在安全敏感场景下应该避免使用后者。这种认知的问题在于:两者底层使用的是完全相同的密码学安全伪随机数生成器(CSPRNG)

从 Linux 4.8 开始,/dev/urandom 的输出直接来自 CSPRNG,而非从熵池中「消耗」熵值。在此之前,两者共享同一个 CSPRNG 核心,唯一的区别是行为策略:当内核估计的熵值不足时,/dev/random 会阻塞等待,而 /dev/urandom 不会。它们输出的随机数在密码学强度上是等价的。

密码学家 Thomas Pornin 曾明确指出:"/dev/urandom 产生的数据在现有技术条件下与真随机不可区分。" 这里的「不可区分」正是 CSPRNG 的设计目标 —— 只要种子足够随机,输出就应该无法与真随机序列区分。

熵值估计:一个保守的启发式算法

/dev/random 的阻塞行为依赖于内核的「熵值估计」。但关键在于:熵值无法被精确计量,只能通过启发式算法估算。Linux 内核通过分析硬件事件(如键盘输入、磁盘 I/O、中断到达时间)的到达时间间隔,使用多项式插值模型来估计「惊喜程度」,进而推算熵值。

这种估计必然是保守的。即使系统实际拥有足够的不可预测性,内核的估计器也可能报告「熵值不足」。这就导致 /dev/random 可能在仍有充足熵值的情况下阻塞,造成不必要的可用性问题。

更重要的是,现代密码学认为:约 256 位的初始熵值就足以支撑计算安全级别的随机数生成。一旦 CSPRNG 被充分种子化,即使不再注入新的熵值,其输出在可预见的未来仍然是安全的。这就是为什么「熵耗尽」这个概念在密码学意义上并不构成实际威胁。

真正的风险点:启动阶段的熵饥饿

虽然运行时的「熵耗尽」是伪命题,但系统启动初期的熵饥饿是真实存在的风险。在 Linux 上,/dev/urandom 即使在 CSPRNG 尚未被充分种子化时也会返回数据,这可能导致早期生成的密钥熵值不足。

这个问题在以下场景尤为突出:

  • 无头服务器 / 嵌入式设备:缺乏键盘、鼠标等交互式熵源
  • 虚拟机克隆 / 快照恢复:多个实例可能从相同的 RNG 状态启动
  • 容器环境:共享内核但隔离的熵池状态可能带来不确定性

现代 Linux 发行版通过在关机时保存随机种子并在下次启动时恢复,来缓解启动熵问题。但对于首次启动或裸机部署,仍需要谨慎处理。

现代解决方案:getrandom () 系统调用

Linux 3.17 引入的 getrandom() 系统调用结合了两种设备的优点:

  • 默认行为:阻塞直到熵池初始化完成,之后永不阻塞
  • GRND_RANDOM 标志:模拟 /dev/random 的阻塞行为
  • GRND_NONBLOCK 标志:非阻塞模式,熵不足时返回 EAGAIN

根据 Linux man-pages 的明确建议:「除非你在生成长期密钥(甚至那时也可能不需要),否则不应该使用 /dev/random 或 GRND_RANDOM。相反,应该使用 /dev/urandom 或不带标志的 getrandom ()。」

可落地的选择策略

基于以上分析,以下是针对不同场景的实践建议:

常规加密操作(会话密钥、临时 nonce、盐值):

  • 直接使用 /dev/urandom 或 getrandom() 默认模式
  • 避免使用 /dev/random,阻塞不会提升安全性,只会降低可用性

长期密钥生成(PGP、SSH、TLS 证书密钥):

  • 确保系统已运行足够时间积累熵值
  • 在虚拟机环境中,确保每个实例都有独立的初始种子
  • 考虑使用硬件 RNG(如 RDRAND)作为额外熵源

嵌入式 / 无头设备启动阶段

  • 延迟关键密钥生成,直到熵池充分初始化
  • 使用 getrandom() 而非直接读取 /dev/urandom,确保阻塞到就绪
  • 考虑集成硬件熵源或网络熵收集服务

容器化环境

  • 避免在容器内直接依赖 /dev/urandom 的启动时行为
  • 在宿主机层面确保熵池质量,或使用用户空间 CSPRNG 库

结论

/dev/urandom 的「不安全」标签是一个需要被纠正的历史误解。在绝大多数加密应用场景中,它提供的随机数质量与 /dev/random 完全等同,且不会引入不必要的阻塞风险。真正的安全关注点应该放在启动阶段的熵积累虚拟化环境的 RNG 状态隔离上,而非运行时的「熵耗尽」幻觉。

对于新的代码开发,优先使用 getrandom() 系统调用,它提供了更清晰的语义和更好的跨平台兼容性。对于现有系统,审查是否错误地使用了 /dev/random 导致不必要的性能问题,并评估启动熵策略是否充分。


参考来源

  • Myths about /dev/urandom, 2uo.de
  • random(7) - Linux manual pages, man7.org

security

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com