跳转到主要内容

密码生成中的随机数质量

本文约需 8 分钟阅读

决定密码生成工具安全性的最关键因素是所使用随机数的质量。即使字符串看起来是随机的,如果生成所用的随机数可以预测,攻击者就能猜出密码。NIST SP 800-90A 是定义密码学随机数生成器要求的标准规范,用于密码生成的随机数必须满足该标准。截至 2025 年,随着量子计算机的发展,NIST 正在推进后量子密码 (PQC) 标准化,随机数生成的安全标准未来也可能被修订。本文从技术角度解析随机数的种类和质量差异,并介绍 Passtsuku.com 采用的安全随机数生成方式。结合密码熵的概念一起理解,可以看清密码强度的本质。

Math.random() 的危险性

在 JavaScript 中生成随机数最简单的方法是 `Math.random()`。但是,这个函数绝对不能用于密码生成。`Math.random()` 是伪随机数生成器 (PRNG: Pseudo-Random Number Generator),它从内部状态(种子值)确定性地通过算法生成数列。

`Math.random()` 的具体问题如下:

  • 内部状态可推测:有时可以从输出的随机数列反推内部状态
  • 无密码学安全性保证:ECMAScript 规范对随机数质量没有任何保证
  • 各浏览器实现不同:V8 引擎 (Chrome) 使用 xorshift128+ 算法,不适合密码学用途
  • 种子值可预测:部分实现使用时间戳作为种子,如果知道生成时间就可能重现输出

安全研究人员已经实证了从 `Math.random()` 输出恢复内部状态的攻击方法。2015 年发表的研究表明,对于 V8 引擎的 xorshift128+ 实现,仅需 64 个连续输出值就能完全恢复内部状态。用 `Math.random()` 生成的密码看起来是随机的,但对攻击者来说只是可预测的字符串。

一个容易被忽视的注意点是,`Math.random()` 的输出范围是 0 以上 1 以下的浮点数,内部仅使用 52 位尾数。也就是说,可输出的值种类最多为 2 的 52 次方(约 4.5 × 10 的 15 次方)个。由于密码学用途需要 128 位以上的熵,`Math.random()` 单独使用在位数上根本不足。

CSPRNG 的工作原理

适合密码生成的随机数必须由密码学安全伪随机数生成器 (CSPRNG: Cryptographically Secure Pseudo-Random Number Generator) 生成。CSPRNG 满足以下两个性质:

  • 不可预测性:即使知道所有过去的输出,也无法预测下一个输出
  • 不可逆推性:从输出反推内部状态在计算上不可行

CSPRNG 使用操作系统收集的熵源作为种子。熵源包括硬件电气噪声、鼠标移动、键盘输入时序、磁盘 I/O 时序等物理上不可预测的事件。通过密码学哈希函数和分组密码处理这些不可预测的数据,生成高质量的随机数列。

Linux 提供 `/dev/urandom`,Windows 提供 BCryptGenRandom 作为操作系统级别的 CSPRNG。这些都是经过多年安全审计和实践验证的可靠实现。关于密码学随机数生成的理论背景,密码学伪随机数与安全设计的解说书 (Amazon)也可供参考。

Web Crypto API: crypto.getRandomValues()

在浏览器环境中使用 CSPRNG 的标准方法是 Web Crypto API 的 `crypto.getRandomValues()`。该 API 由 W3C 标准化,在所有现代浏览器中均可使用。

`crypto.getRandomValues()` 直接调用操作系统提供的 CSPRNG 来生成随机数。也就是说,获取的是经过操作系统级别验证的密码学安全随机数,而非浏览器自有的算法。

与 `Math.random()` 的决定性区别在于,`crypto.getRandomValues()` 保证了密码学安全性。从输出推测内部状态在计算上不可行,即使知道过去的输出也无法预测未来的输出。

不过,`crypto.getRandomValues()` 也有限制。单次调用可获取的最大字节数限制为 65,536 字节,超过此限制的请求会抛出异常。在密码生成用途中几乎不会达到此限制,但在需要大量随机数的模拟用途中需要注意。

作为验证随机数质量的方法,NIST SP 800-22 定义的统计测试套件被广泛使用。该测试套件包含频率测试、游程测试、线性复杂度测试等 15 种检验。`crypto.getRandomValues()` 的输出通过了所有这些测试,而 `Math.random()` 的输出已知在多项检验中不合格。关于随机数质量的评估方法,NIST 随机数检验与密码学评估技术书 (Amazon)也可供参考。

Passtsuku.com 的随机数生成方式

Passtsuku.com 在密码生成的所有步骤中都使用 `crypto.getRandomValues()`,完全不使用 `Math.random()`。这保证了生成密码的每个字符都基于密码学安全的随机数进行选择。

均匀分布的保证

除了随机数质量外,Passtsuku.com 还注重字符选择的均匀性。如果将 `crypto.getRandomValues()` 返回的值简单地对字符集大小取模,当字符集大小不是 2 的幂时会产生轻微偏差。例如,将 0-255 范围的随机数对 62 种字符集(大小写字母 + 数字)取模时,前 8 个字符被选中的概率比其他字符高约 1.6%。Passtsuku.com 采用拒绝采样法来消除这种偏差,确保所有字符以完全相等的概率被选择。

浏览器内完成的安全性

Passtsuku.com 的随机数生成和密码生成全部在浏览器内完成。生成的密码和随机数种子值不会通过网络发送到外部。由于不涉及服务器端处理,通信路径上的拦截风险为零。

选择密码生成工具时,确认其采用何种随机数生成方式非常重要。使用 `Math.random()` 的工具存在安全隐患,请选择像 Passtsuku.com 这样使用 `crypto.getRandomValues()` 的工具。

随机数质量自检清单

选择密码生成工具时,请从以下角度确认随机数质量:

  • 工具是否使用 `crypto.getRandomValues()` 或同等的 CSPRNG
  • 是否明确声明不使用 `Math.random()`
  • 生成处理是否在浏览器内(客户端)完成
  • 字符选择是否采用拒绝采样等消除偏差的方法
  • 源代码是否公开,能否验证随机数生成方式

Passtsuku.com 满足以上所有条件。理解的概念,掌握与哈希函数的关系,可以更深入地理解随机数质量如何影响密码强度。关于如何应用这些原理,请参阅安全密码创建指南加密基础知识。高质量的随机数正是使密码能够抵御暴力破解攻击的关键。

现在就能做的事

  1. 使用 Passtsuku.com 生成 16 个字符以上的密码,替换当前使用的"自己想的"密码
  2. 确认正在使用的密码生成工具是否使用 crypto.getRandomValues()(或同等的 CSPRNG)
  3. 使用 Passtsuku.com 的强度计确认生成的密码具有 80 位以上的熵
  4. 将浏览器更新到最新版本,确保可以使用最新的 Web Crypto API 实现

常见问题

用 Math.random() 生成密码安全吗?
不安全。Math.random() 不是加密安全的随机数生成器,其输出模式是可预测的。请使用 Web Crypto API 的 crypto.getRandomValues() 来生成密码。
加密安全随机数和普通随机数有什么区别?
普通伪随机数由种子值决定,知道种子就能预测所有输出。加密安全随机数使用硬件熵源,从过去的输出在计算上无法预测未来的输出。
如何验证密码生成器的随机数质量?
对于开源工具,可以检查是否使用了 crypto.getRandomValues() 或 /dev/urandom 等加密安全 API。对于闭源工具,可以通过大量生成密码并检查字符频率偏差来进行统计验证。

这篇文章对您有帮助吗?

相关术语

XHatena