关于 /dev/urandom 的常见误解 (2014)

Hacker News Top 工具

摘要

澄清了关于 /dev/urandom 和 /dev/random 的常见误解,说明 /dev/urandom 是类 Unix 系统中加密随机性的首选来源。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/05/14 12:23

# 关于 /dev/urandom 的迷思 来源:https://www.2uo.de/myths-about-urandom/ 关于 /dev/urandom 和 /dev/random 有一些说法被反复提及,但它们仍然是错误的。 ## /dev/urandom 不安全,在密码学用途中应始终使用 /dev/random 事实:`/dev/urandom` 是类 UNIX 系统上加密随机数的首选来源。 ## /dev/urandom 是伪随机数生成器(PRNG),而 /dev/random 是“真”随机数生成器 事实:(https://www.2uo.de/myths-about-urandom/#structure-of-linuxs-random-number-generator) `/dev/urandom` 和 `/dev/random` 使用的是完全相同的 CSPRNG(密码学安全伪随机数生成器)。它们的少量差异与“真”随机性毫无关系。 ## /dev/random 无疑是加密领域的更好选择。即使 /dev/urandom 同样安全,也没有理由选择后者 事实:(https://www.2uo.de/myths-about-urandom/#whats-wrong-with-blocking) `/dev/random` 有一个非常讨厌的问题:它会阻塞。 ## 但那很好!/dev/random 给出的随机数恰好等于其池中的熵量。/dev/urandom 即使熵早已耗尽,也会给出不安全的随机数 事实:(https://www.2uo.de/myths-about-urandom/#what-about-entropy-running-low) 不。即使忽略可用性及后续被用户操纵等问题,熵“耗尽”本身就是一个稻草人谬误。大约 256 比特的熵足以在很长一段时间内产生计算上安全的数字。 而有趣的事情才刚刚开始:`/dev/random` 是如何知道有多少熵可以给出呢?请继续往下看! ## 但密码学家们总是谈论不断重新播种。这与你的最后一点不是矛盾吗? 事实:(https://www.2uo.de/myths-about-urandom/#re-seeding) 被你问住了!有点道理。确实,随机数生成器会被系统能获取到的任何熵不断重新播种。但这(部分)有其他原因。 听我说,我并不是说注入熵是坏事。它很好。我只是说在熵估算值较低时阻塞是不好的。 ## 这一切都很好,但即使是 /dev/\(u\)random 的手册页也反驳了你!懂这方面的人真的同意你吗? 事实:(https://www.2uo.de/myths-about-urandom/#the-random-and-urandom-man-page) 不,它并没有。它似乎暗示 `/dev/urandom` 在加密使用中是不安全的,除非你真正理解所有那些密码学术语。 手册页确实在某些情况下推荐使用 `/dev/random`(在我看来并非有害,但严格来说并非必要),但同时也推荐将 `/dev/urandom` 用于“常规”加密用途。 虽然诉诸权威通常不值得骄傲,但在密码学问题上,你通常应该谨慎并尝试听取领域专家的意见。 是的,有不少专家 (https://www.2uo.de/myths-about-urandom/#orthodoxy) 认同我的观点:在类 UNIX 系统的密码学上下文中,`/dev/urandom` 是随机数需求的首选方案。显然,他们的意见影响了我,而不是相反。 很难相信,对吧?我一定是错的!好吧,继续读下去,让我试着说服你。 在真正讨论所有这些要点之前,恐怕有两项预备工作需要先处理好。 即,什么是随机性 (https://www.2uo.de/myths-about-urandom/#true-randomness),或者更确切地说:我这里谈论的是哪种随机性? 此外,更重要的是,我确实不是 (https://www.2uo.de/myths-about-urandom/#youre-saying-im-stupid) 在居高临下地指责你。我写这份文档是为了当这个话题再次出现时,有一个可以指向的东西。超过 140 个字符。不用一遍又一遍地重复自己。能够锤炼写作和论证本身,使许多场合的讨论受益。 我当然也愿意听取不同意见。我只是说,仅仅声称 `/dev/urandom` 不好是不够的。你需要指出你不同意的具体观点并展开辩论。 ## 你说我很蠢! 强调:**不**! 事实上,几年前我自己也曾相信 `/dev/urandom` 是不安全的。而且你和我几乎都不得不相信这一点,因为所有那些在 Usenet、网络论坛以及今天的 Twitter 上备受尊敬的人都这样告诉我们。连手册页似乎都这么说。我们怎能驳斥他们关于“熵耗尽”的有力论证呢? 这种误解之所以如此泛滥,不是因为人们愚蠢,而是因为对密码学稍有了解(比如对熵的模糊概念)就很容易被说服。直觉几乎把我们推向那里。不幸的是,直觉在密码学中往往是错误的。这里也是如此。 ## 真正的随机性 随机数“真正随机”是什么意思? 我不想深入探讨这个问题,因为它很快就会变得哲学化。讨论往往迅速失控,因为每个人都可以滔滔不绝地谈论自己最喜欢的随机性模型,而不关注其他人在说什么,甚至不让自己被理解。 我相信“真正随机性”的“金标准”是量子效应。观察光子穿过半透明镜——或者不穿过。观察某些放射性物质发射α粒子。这是我们在世界上随机性方面拥有的最好概念。其他人可能有理由相信这些效应并非真正随机。甚至认为世界上根本没有随机性。百花齐放吧。 密码学家常常回避这场哲学辩论,他们不在乎随机性“真正”意味着什么。他们关心的是不可预测性。只要没有人能获得关于下一个随机数的任何信息,我们就没问题。当你谈论随机数作为使用密码学的前提时,我认为这就是你应该追求的目标。 不管怎样,我不太关心那些“哲学上安全”的随机数,我把你的“真正”随机数视为这类东西。 ## 两种安全性,一种重要 但假设你已经获得了这些“真正”随机的数字。你打算用它们做什么? 你把它们打印出来,装裱起来挂在客厅墙上,陶醉在量子宇宙的美感中?那很好,我当然理解。 等等,什么?你要使用它们?用于加密目的?好吧,那可就糟了,因为事情变得有点棘手了。 你看,你那些真正随机、受量子效应祝福的随机数,被放入了一些不太体面的、现实世界中被玷污的算法中。 因为我们使用的几乎所有密码算法都不具备**信息论安全性**。它们“只能”提供**计算安全性**。我想到的两个例外是 Shamir 的秘密共享和一次性密码本。虽然前者可能是一个有效的反例(如果你真的打算使用它),但后者完全不切实际。 但你所熟知的所有算法——AES、RSA、Diffie-Hellman、椭圆曲线,以及你使用的所有加密包——OpenSSL、GnuTLS、Keyczar、你的操作系统加密 API——这些都只是计算安全的。 有什么区别?信息论安全的算法是绝对安全的,而其他算法无法保证在面对拥有无限计算能力、尝试所有可能密钥的对手时是安全的。我们仍然使用它们,因为世界上所有计算机加在一起穷举密钥所需的时间比宇宙已存在的时间还要长。这就是我们在这里谈论的“不安全”程度。 除非某个聪明人用少得多的计算能力攻破了算法本身——甚至是今天就能达到的计算能力。这是每个密码分析者梦寐以求的大奖:攻破 AES 本身、攻破 RSA 本身,等等。 所以现在到了这个地步:你不信任随机数生成器内部的构建块,坚持要“真正随机性”而不是“伪随机性”。但随后你却在那些你如此鄙视以至于不肯让它们靠近随机数生成器的算法中使用这些“真正”的随机数! 事实是,当最先进的哈希算法被攻破,或者当最先进的块密码被攻破时,你因为它们而得到“哲学上不安全”的随机数已经不重要了。你已经没有可以用来安全使用它们的东西了。 所以,只需为你的计算安全算法使用那些计算安全的随机数。换句话说:使用 `/dev/urandom`。 ## Linux 随机数生成器的结构 ### 一种错误观点 很可能你对内核随机数生成器的理解与此类似: 内核随机数生成器的神话结构:“真正随机性”(尽管可能有偏差和偏斜)进入系统,其熵被精确计数并立即加入内部熵计数器。经过去偏和自化后,它进入内核熵池,`/dev/random` 和 `/dev/urandom` 都从该池中获取随机数。 “真正”随机数生成器 `/dev/random` 直接从池中取出这些随机数(如果熵计数足够满足请求数量),当然会减少熵计数器。如果不够,它会阻塞,直到新熵进入系统。 这个叙述中重要的是,`/dev/random` 基本上输出了外部随机源输入的数字,仅经过必要的自化。仅此而已,纯粹的随机性。 故事继续讲,`/dev/urandom` 做同样的事情。除了当系统中没有足够的熵时。与 `/dev/random` 不同,它不会阻塞,而是从一个与其余随机数机制一起运行的伪随机数生成器(当然,是密码学安全的)中获得“低质量随机”数。这个 CSPRNG 只是被随机池中的“真正随机性”播种一次(或者偶尔播种,没关系),但你不能真正信任它。 在这种观点下(似乎很多人在谈论 Linux 上的随机数时都持有这种看法),避免使用 `/dev/urandom` 是合理的。 因为要么还有足够的熵剩余,这时你得到与 `/dev/random` 相同的结果;要么没有足够熵,这时你从一个几乎从未见过高熵输入的 CSPRNG 中得到那些低质量随机数。 很阴险,对吧?不幸的是,这也完全错误。实际上,随机数生成器的内部结构是这样的: ### 一个更好的简化 #### Linux 4.8 之前 Linux 4.8 之前内核随机数生成器的实际结构 看到大区别了吗? CSPRNG 并非与随机数生成器并行运行,在 `/dev/urandom` 想输出但没有什么好输出时填补空缺。CSPRNG 是随机数生成过程中不可分割的一部分。不存在 `/dev/random` 直接从自化器分发“好且纯”的随机数。每个随机源的输入在被输出为随机数(无论通过 `/dev/urandom` 还是 `/dev/random`)之前,都会在 CSPRNG 内被彻底混合和哈希。 另一个重要区别是,这里没有熵计数,而是估算。某个源给你的熵量并不是你随着数据一起就能轻易得到的东西。它需要估算。请注意,当你的估算过于乐观时,`/dev/random` 备受推崇的特性——即只给出与可用熵量相等的随机数——就消失了。不幸的是,估算熵的量是很困难的。 Linux 内核仅使用事件的到达时间来估算其熵。它通过对这些到达时间进行多项式插值,根据模型计算实际到达时间的“意外程度”。这种多项式插值模型是否是估算熵的最佳方式是一个有趣的问题。还存在一个问题:内部硬件限制可能会影响这些到达时间。各种硬件组件的采样率也可能起作用,因为它们直接影响事件到达时间的数值和粒度。 最终,据我们所知,内核的熵估算相当不错。这意味着它是保守的。人们争论它到底有多好,但这个问题远远超出了我的理解范围。尽管如此,如果你坚持绝不给出没有足够熵“支撑”的随机数,你可能会感到不安。我则睡得很安稳,因为我不关心熵估算值。 所以,把一件事说清楚:`/dev/random` 和 `/dev/urandom` 都是由同一个 CSPRNG 提供的。只有根据某种估算,当各自对应的池中熵耗尽时的行为不同:`/dev/random` 阻塞,而 `/dev/urandom` 不阻塞。 #### 从 Linux 4.8 开始 在 Linux 4.8 中,放弃了 `/dev/urandom` 和 `/dev/random` 之间的等价性。现在 `/dev/urandom` 的输出不再来自熵池,而是直接来自一个 CSPRNG。 从 Linux 4.8 开始的内核随机数生成器实际结构 我们很快就会看到 (https://www.2uo.de/myths-about-urandom/#the-csprngs-are-alright) 为什么这不是安全问题。 ## 阻塞有什么问题? 你是否曾等待过 `/dev/random` 给你更多随机数?也许是在虚拟机内生成 PGP 密钥?或者连接到一个正在等待更多随机数以创建临时会话密钥的 Web 服务器? 这就是问题所在。它天生与可用性相悖。所以你的系统无法工作。它没有执行你构建它的目的。显然,这很糟糕。如果你不需要它,你当初就不会构建它。 但问题甚至更深:人们不喜欢被阻止前进的步伐。他们会想出变通办法,编造古怪的机制来让系统运行起来。那些对密码学一无所知的人。普通人。 为什么不直接去掉 random() 调用呢?为什么不让某个网络论坛的人告诉你如何使用一个奇怪的 ioctl 来增加熵计数器呢?为什么不干脆关掉 SSL 呢? 最终,你只会教会你的用户做一些愚蠢的事情,从而在你毫不知情的情况下破坏系统的安全性。 很容易忽略可用性、易用性或其他优良属性。安全压倒一切,对吧?所以宁愿不方便、不可用、不好用,也比伪装安全好。 但这是一个错误的二分法。阻塞对于安全并非必要。正如我们所见 (https://www.2uo.de/myths-about-urandom/#a-better-simplification),`/dev/urandom` 提供与 `/dev/random` 相同类型的随机数,直接来自一个 CSPRNG。使用它! ## CSPRNG 没问题 但如今一切听起来真的很暗淡。如果连来自 `/dev/random` 的高质量随机数都来自一个 CSPRNG,我们如何能将其用于高安全性目的? 事实证明,“看起来随机”是我们许多加密构建块的基本要求。如果你取一个加密哈希的输出,它必须与随机字符串无法区分,以便密码学家接受它。如果你取一个块密码,其输出(在不知道密钥的情况下)也必须与随机数据无法区分。 如果任何人能够利用这些 CSPRNG 相对于“真正”随机性的某种感知弱点,在暴力破解加密构建块方面获得优势,那么还是那个老故事:你没有任何东西剩下。块密码、哈希、公钥算法——一切都完了。所以,你因为不信任 CSPRNG 而选择在加密中使用“真正”随机数,但你却在算法中使用它们,而这些算法恰恰依赖于与 CSPRNG 输出无法区分的特性。这说不通。 ## 那么熵估算呢? 熵估算是对可用于为 CSPRNG 播种的不可预测性的度量。它很难正确完成。而且 Linux 内核的估算方法可能并不完美。但重要的是:一旦 CSPRNG 被播种,它就会持续产生安全的随机数,远超过你从硬件源获得的任何熵。你不需要持续注入熵来保持其安全。注入熵可以增加安全性,但并非必需——增加的是防止潜在未来攻击的余量。 所以,即使内核的熵估算不准确,`/dev/urandom` 的安全性也不会降级。它可能比估计的要弱,但不会比 `dev/random` 弱,因为两者使用相同的 CSPRNG。唯一的问题是阻塞行为 ## 那 `getrandom()` 系统调用呢? Linux 3.17 引入了 `getrandom()` 系统调用。它的行为类似于打开 `/dev/urandom` 并从中读取,但有一个区别:如果内核熵池尚未初始化(即系统刚启动不久),它会阻塞。一旦初始化完成,它就不会再阻塞。这是 `/dev/urandom` 行为的一个更安全版本,适用于需要保证随机数生成器已经初始化的场景。 ## 结论 所以,请使用 `/dev/urandom`。它安全、快速、永不阻塞。它在密码学上足够安全,并且是类 UNIX 系统上随机数需求的首选。 ## 补充说明 这篇文章的目的是澄清误解。我理解有些人可能仍然坚持使用 `/dev/random`。但请记住:阻塞行为并非优点,而是一个可用性问题。而且,`/dev/urandom` 并不像一些老式建议所说的那样“不安全”。实际上,它被广泛认为是正确的选择。 ## 参考资料 - [Linux 内核随机数生成器文档](https://www.kernel.org/doc/html/latest/core-api/random.html) - [Myths about /dev/urandom](https://www.2uo.de/myths-about-urandom/)(原始文章) (注:以上翻译尽量保留了原文的链接和格式,专业术语保持英文或使用中文通用译法。)

相似文章

什么是随机生成?

Lobsters Hottest

本文探讨了计算机中的伪随机数生成,重点聚焦于线性同余生成器(LCG)及其质量可视化。文章还提及了 Cloudflare 的熔岩灯等熵源,并作为基于属性的测试的前导内容。

具备发现 Bug 概率保证的随机调度器

Lobsters Hottest

Microsoft Research 的这篇论文介绍了一种随机调度技术,旨在为发现软件系统中的 Bug 提供概率性保证。该成果已发表于 ASPLOS 会议,核心在于利用算法随机性来实现系统化的故障检测。

有时需要随机性来实现协调

arXiv cs.AI

本文介绍了 Diamond Attention,这是一种用于多智能体强化学习的方法,通过引入结构化随机性来打破对称性,从而实现同质智能体之间的角色区分,在 XOR 游戏等对称任务中实现了完美的协调。