一篇讨论 Unix/Linux 随机数设备的技术文章,又把一个老问题翻了出来:生成密钥、令牌、会话随机数时,到底该读 /dev/random,还是 /dev/urandom?

很多人的直觉是,名字里少了一个 u 的 /dev/random 更“真”,会阻塞也说明更谨慎。但这个直觉很容易误导人。初始化完成后的现代类 Unix 系统里,/dev/urandom 通常才是密码学随机数的推荐来源。

这事不只是手册页里的小差异。TLS 会话密钥、SSH 密钥、证书生成、容器启动、数据库令牌,都可能依赖系统随机源。选错接口,不一定更安全,却可能让服务卡住。

更麻烦的是,服务一旦卡住,工程团队常会找捷径。手工改熵计数、换弱随机函数、关闭某个安全检查,这些补丁看起来是在救火,实际可能是在挖坑。

误解在哪里:不是“真随机 vs 伪随机”

最常见的误解,是把 /dev/random 和 /dev/urandom 分成两类:前者是真随机,后者是伪随机。

这个说法太粗。历史上,两者都依赖内核里的密码学安全伪随机数生成器,也就是 CSPRNG。它不是普通 PRNG,设计目标就是让攻击者在不知道内部状态和种子的情况下,无法预测输出。

两者的核心差异,主要在阻塞行为。内核认为熵估计不足时,/dev/random 可能阻塞;/dev/urandom 在随机数生成器初始化完成后,通常不会因为这个估计值继续卡住调用者。

对比项/dev/random/dev/urandom更准确的理解
输出来源内核随机数机制 / CSPRNG内核随机数机制 / CSPRNG不是一个纯真随机、一个普通伪随机
主要差别可能因熵估计阻塞初始化后通常不阻塞阻塞不是安全性的同义词
常规密码学用途不一定更合适通常是推荐来源前提是现代系统且已初始化
主要风险启动慢、服务卡死、批量部署受阻早期启动阶段需确认初始化两边都不能脱离系统状态谈绝对安全

熵也不是仓库里的库存,不能像数硬币一样实时清点。内核只能估计键盘、磁盘、网络、硬件随机源等事件里有多少不可预测性。

所以,“熵用完了”经常被误用。它听起来像随机数被消耗完了,其实更接近一个保守估计值下降了。

只要 CSPRNG 已经拿到足够高质量的种子,约 256 bit 熵就足以支撑长期的计算安全输出。持续注入熵当然有益,但这不等于估计值一低,系统就必须停下来等“新随机”。

这也是这场争论的主线:问题不是“真随机是否更高级”,而是阻塞式 /dev/random 在实际密码学场景中,是否真的比 /dev/urandom 更安全、更合适。

我的判断是,通常不是。

机制要看边界:初始化完成后,/dev/urandom 才好用

这里要留一个重要边界:不能把 /dev/urandom 写成任何时候、任何系统、任何启动阶段都绝对安全。

更稳妥的说法是:在现代系统、内核随机数生成器已经初始化、系统随机机制正常的情况下,/dev/urandom 通常适合密码学用途。

早期启动阶段是例外风险。比如极简系统刚启动、嵌入式设备缺少随机事件、虚拟机镜像被克隆后没有重新播种,这些场景都需要额外检查。

Linux 引入 getrandom(2) 的现实意义,就在这里。程序可以等待内核随机数生成器初始化完成,而不是自己直接读设备文件,再猜当前状态是否安全。

对开发者来说,动作应该更明确:

  • 写新代码时,优先用操作系统或语言标准库提供的密码学安全随机接口。
  • Linux 上,优先考虑 getrandom(2) 或语言运行时对它的封装。
  • 不要为了“看起来更真”,默认去读 /dev/random。
  • 不要自己写 PRNG 来替代系统随机源。

对安全工程师来说,评审重点也要换。别只问“用了 random 还是 urandom”,更要问:随机数生成器是否已初始化?虚拟机克隆后是否重新播种?容器和极简镜像有没有正确继承系统随机源?

这比盯着设备文件名更有用。

真正的成本:阻塞会逼出更危险的绕过

/dev/random 的问题,常常不在密码学论文里,而在生产系统里。

云主机批量启动、虚拟机里生成 SSH 或 PGP 密钥、Web 服务创建临时密钥,都可能碰到随机源阻塞。系统卡住时,最先承压的是值班工程师和发布流程。

这时,组织里的安全决策会变形。一个阻塞问题如果挡住上线,团队很可能先追求“让服务起来”。于是,弱随机函数、手工调参数、关闭安全功能,就有了进入生产环境的机会。

这就是 /dev/random 被高估的地方。它把安全感放在“等待”这个动作上,但等待本身并不自动制造更高质量的密码学随机数。等待还会制造可用性压力。

相关团队可以按对象分两类处理:

角色应该做什么不该做什么
Linux/Unix 系统开发者用 getrandom(2)、arc4random、SecRandomCopyBytes 或语言安全随机接口直接在业务代码里纠结读 /dev/random 还是 /dev/urandom
运维与安全工程师检查启动阶段初始化、镜像克隆播种、容器随机源继承用改熵计数、换弱随机函数来解决阻塞

不同系统的推荐接口不完全一样。OpenBSD 长期推荐 arc4random 系列接口;Apple 平台有 SecRandomCopyBytes;Linux 上更常见的路线是 getrandom(2) 及其语言封装。

这些接口的方向是一致的:把随机数复杂性收进内核和基础库,减少业务工程师直接碰设备文件的机会。

接下来最该观察的,也不是哪篇文章把 /dev/random 说得更神秘,而是发行版、语言运行时和安全库有没有继续减少误用空间。比如默认 API 是否会等待初始化完成,容器和虚拟机环境是否处理好播种,文档是否还在暗示“会阻塞就更安全”。

回到开头那个问题:该不该迷信会阻塞的 /dev/random?

不该。安全工程里,可预测性是敌人,误用也是敌人。把接口选得更难用,不会自动让系统更安全。