Math.random vs crypto.getRandomValues
对比 Math.random 和 crypto.getRandomValues 的安全性、适用场景与使用方式。
#type / synthesis
#status / evergreen
#resource / javascript
#tech / dev / frontend
#platform / browser
#tech / security
[!info] related notes
- 所属 MOC: 安全 MOC
- 原子概念: crypto.getRandomValues,
Math.random- 应用场景: Fisher-Yates 洗牌算法
Math.random vs crypto.getRandomValues
为什么要把这两个放在一起理解
两者都是 JavaScript 中生成随机数的方式,但安全等级完全不同。在需要安全性的场景(密码、令牌、密钥)中用错,会导致严重的安全漏洞。
核心对比
| 维度 | Math.random() | crypto.getRandomValues() |
|---|---|---|
| 算法 | 伪随机(xorshift128+ 等) | 密码学安全(CSPRNG) |
| 随机源 | 纯算法,种子可推算 | 操作系统熵池(硬件噪声等) |
| 可预测性 | 收集足够输出可推算后续值 | 不可推算 |
| 速度 | 更快 | 稍慢(实际差异可忽略) |
| 全局可用 | 所有 JS 环境 | 浏览器 + Node.js 19+ |
使用边界
用 Math.random() 的场景
- 动画效果的随机抖动
- 游戏中的非关键随机(如粒子特效)
- 非安全的 UI 随机化(如随机背景色)
必须用 crypto.getRandomValues() 的场景
- 生成用户密码
- 生成 CSRF Token
- 生成 API Key / Secret
- 生成 Session ID
- 任何需要防止预测的安全场景
常见错误
❌ 用 Math.random 生成密码
// 危险!密码可被预测
function generatePassword(length) {
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
let password = '';
for (let i = 0; i < length; i++) {
password += chars[Math.floor(Math.random() * chars.length)];
}
return password;
}
❌ 用 sort + Math.random 洗牌
// 错误!分布不均匀
const shuffled = arr.sort(() => Math.random() - 0.5);
✅ 安全的实现
function getSecureRandomInt(max: number): number {
const array = new Uint32Array(1);
crypto.getRandomValues(array);
return array[0] % max;
}
function secureShuffle(array: string[]): string[] {
const result = [...array];
for (let i = result.length - 1; i > 0; i--) {
const j = getSecureRandomInt(i + 1);
[result[i], result[j]] = [result[j], result[i]];
}
return result;
}
学习顺序建议
- 先理解
Math.random为什么不安全(伪随机原理) - 再学
crypto.getRandomValues的用法和兼容性 - 最后掌握 Fisher-Yates 算法 + 安全随机数的组合