Math.random() 在中奖概率计算中的安全问题
Math.random() 与 crypto.getRandomValues() 的区别及使用场景
问题
使用 Math.random() 计算中奖概率是否存在安全风险?什么场景下应该使用 crypto.getRandomValues()?
解答
Math.random() 的适用场景
对于普通的随机需求,Math.random() 完全够用:
生成随机 ID:
document.body.id = ('_' + Math.random()).replace('0.', '');
随机排序:
[1, 2, 3, 4, 5].sort(_ => Math.random() - .5);
Math.random() 的安全风险
Math.random() 不安全的原因与”伪随机”无关(getRandomValues() 也是伪随机),而是其实现机制导致的:
底层实现机制:
- V8 引擎使用 xorshift128+ 算法生成随机数
- 为了性能,一次生成 64 个随机数并缓存
- 算法公开且源码可见,可被其他语言模拟
安全隐患:
攻击者如果获知当前随机生成器的状态,就能推算出缓存中的所有随机数。在抽奖、加密等场景中,这会导致严重的安全问题。
使用 crypto.getRandomValues()
基本用法:
let randNumber = self.crypto.getRandomValues(new Uint32Array(1))[0];
// 返回一个随机整数,通常 10 位
console.log(randNumber);
语法:
crypto.getRandomValues(typedArray)
支持的类型数组:Int8Array、Uint8Array、Int16Array、Uint16Array、Int32Array、Uint32Array
优化封装:
Math.randomValue = function () {
return self.crypto.getRandomValues(new Uint32Array(1))[0];
};
getRandomValues() 的特点
更安全的随机源:
- 使用系统层面的无序源(部分硬件自带随机种子)
- 实时生成,无缓存机制
- 不同浏览器实现可能不同
性能考虑:
由于实时生成,性能比 Math.random() 差。高并发场景下,如果随机数仅用于普通随机(非安全相关),应优先使用 Math.random()。
使用建议
必须使用 getRandomValues() 的场景:
- 生成密钥、token
- 抽奖活动
- 涉及金钱的随机计算
- Node.js 服务端加密操作
可以使用 Math.random() 的场景:
- 随机 ID 生成
- 数组随机排序
- UI 动画随机效果
- 其他与安全无关的随机需求
关键点
Math.random()使用缓存机制,算法公开可预测,不适合安全场景crypto.getRandomValues()实时生成随机数,使用系统级随机源,更安全但性能较差- 涉及金钱、加密、抽奖等场景必须使用
getRandomValues() - 普通随机需求(如随机 ID、排序)使用
Math.random()即可 - 前端几乎不存在高并发问题,可以放心使用
getRandomValues()
目录