当我们需要在程序中生成一个随机数时,Math.random() 往往是开发者的首选。它简单、直接,一行代码就能得到一个 0 到 1 之间的浮点数。
然而,这个看似万能的工具,却藏着一个关乎安全的致命缺陷。
Math.random() 的“原罪”:它是可预测的
Math.random() 生成的数字并非真正的随机,而是伪随机。
什么是伪随机?它是由一个确定的算法,根据一个初始值(称为“种子”)计算出来的一系列数字。这个算法本身是公开的,这意味着,如果你知道了初始的“种子”,你就能完全预测出接下来生成的每一个“随机数”。
在早期的浏览器中,这个“种子”甚至可能只是简单的时间戳,使得预测变得非常容易。虽然现代浏览器已经改进了种子的生成方式,使其更难被猜测,但 Math.random() 的核心机制并没有改变。ECMAScript 规范本身不要求 Math.random() 必须是密码学安全的。这意味着,对于涉及令牌生成、抽奖或加密等场景,使用它是一个安全隐患。
更安全的替代方案:crypto.getRandomValues()
window.crypto 是浏览器提供的一套用于密码学操作的 API,而 crypto.getRandomValues() 就是其中的一员。它是一个密码学安全伪随机数生成器 (CSPRNG)。
与 Math.random() 不同,crypto.getRandomValues() 的设计目标就是提供密码学级别的安全性。
它是如何做到“真正随机”的?
它直接从操作系统底层获取高质量的“熵 (Entropy)”。这些熵的来源是不可预测的物理事件,例如:
- 鼠标移动的精确时机和轨迹
- 键盘输入的时机
- 硬件设备产生的微小噪声
- 网络数据包的到达时间
操作系统将这些不可预测的事件混合成一个“熵池”,crypto.getRandomValues() 正是从这个池中获取随机性,使其生成的数值在统计学上是真正不可预测的。
如何使用 crypto.getRandomValues()?
它的用法与 Math.random() 有所不同。它不是直接返回一个数字,而是用于填充一个类型化数组 (Typed Array),如 Uint8Array 或 Uint32Array。
基础用法:
// 创建一个包含 10 个字节的数组
const randomBytes = new Uint8Array(10);
// 用密码学安全的随机值填充它
crypto.getRandomValues(randomBytes);
console.log(randomBytes); // 输出: Uint8Array(10) [185, 20, 248, 119, ...]
这看起来似乎没那么直观,但别担心,我们可以轻松地将它封装成我们习惯使用的函数。
替代 Math.random() 的函数:
我们可以生成一个 32 位无符号整数,然后将其转换为 0 到 1 之间的浮点数。

生成范围内安全随机整数的函数(常用):
function secureRandomInt(min, max) {
const range = max - min + 1;
// 创建一个足够大的随机数,以减少模偏差
const randomValue = new Uint32Array(1);
crypto.getRandomValues(randomValue);
return min + (randomValue[0] % range);
}
console.log(secureRandomInt(1, 6)); // 模拟安全的骰子
console.log(secureRandomInt(1000, 9999)); // 生成一个安全的 4 位验证码
如何选择:安全场景 vs. 非安全场景
明白了原理和用法,关键在于如何选择。Math.random() 适用于那些不涉及安全或公平性的纯视觉效果或特定玩法场景,例如:
- 生成随机的粒子效果、模拟下雨或下雪
- 创作随机的图案和视觉效果
- 需要玩家通过分享种子来玩到完全相同的游戏关卡
而当你的应用涉及以下安全敏感场景时,请毫不犹豫地选择 crypto.getRandomValues():
- 生成用户会话令牌 (Token) 或 CSRF 令牌
- 创建加密密钥或初始化向量 (IV)
- 进行抽奖、彩票等需要保证绝对公平的随机分配
- 生成高安全性的验证码或一次性密码 (OTP)
目前,crypto.getRandomValues() API 早已兼容各现代浏览器(IE 除外),完全可以投入生产环境使用。在开源社区和技术论坛的讨论中,关于前端安全的实践也越来越受到重视。将默认的 Math.random() 替换为更安全的随机源,正是构建健壮Web应用的一个细小但重要的步骤。希望本文能帮助你在合适的场景做出正确的技术选择,也欢迎到云栈社区的前端板块交流更多关于 Web API 和开发安全的实践。