math.random() 不适合加密场景,因其基于可预测的线性同余生成器;安全场景必须使用 crypto.getrandomvalues() 调用系统熵源。

Math.random() 不能生成安全随机数,它根本不适合加密场景。它的设计目标是简单、快、够用,而不是抗预测、抗重放或防碰撞。
Math.random() 的本质是伪随机
它基于线性同余生成器(LCG),用一个确定性算法从种子推导出后续数值。只要知道当前状态或足够多的输出,就能反推出种子,进而预测全部后续随机数。浏览器中它通常以时间戳为初始种子,但分辨率有限,容易被暴力穷举。
这意味着:
- 同一页面多次刷新,早期生成的随机数可能高度相似
- 攻击者若截获几个 token 或 nonce,可能还原出整个序列
- 无法满足密码学“不可预测性”和“不可重现性”的基本要求
加密场景需要的是真随机或密码学安全随机数
像生成密钥、盐值(salt)、一次性验证码(TOTP)、CSRF token 或加密 IV 这类操作,必须依赖操作系统级熵源——比如硬件噪声、中断时序、鼠标移动轨迹等难以观测和复现的物理信号。
Web 环境下,crypto.getRandomValues() 就是为此而生:
- 调用底层 Web Crypto API,由浏览器委托给操作系统安全模块
- 返回的是
Uint8Array等类型化数组,每个字节都经过密码学验证 - 即使在 iframe 或受限上下文里,只要不是完全禁用 crypto,通常仍可用
两者在代码上一眼可辨
对比生成 1–100 的整数:
// ❌ 不安全:Math.random() 可被推测
function badRandom(min, max) {<br>
return Math.floor(Math.random() * (max - min + 1)) + min;<br>
}
// ✅ 安全:基于系统熵源
function goodRandom(min, max) {<br>
const range = max - min + 1;<br>
const arr = new Uint32Array(1);<br>
crypto.getRandomValues(arr);<br>
return min + (arr[0] % range);<br>
}
什么时候能用 Math.random()?
它依然很有价值,但仅限非安全敏感场景:
- 前端动画中的粒子位置、颜色偏移
- 测试数据填充、UI 模拟(如随机头像、占位文字)
- 游戏内非关键逻辑(如怪物掉落概率,不涉及真实资产)
- AB 实验分组(只要不用于权限控制或计费逻辑)
一旦涉及用户凭证、加密密钥、会话标识、防重放令牌,就必须切换到 crypto。











