我们日常开发经常会用到随机数,基本上我接触下来,都是使用 Math.random() 生成的。
例如生成随机ID:
document.body.id = ('_' + Math.random()).replace('0.', '');
请问这样实现有没有问题?
回答:没有问题。
例如随机排序:
[1, 2, 3, 4, 5].sort(_ => Math.random() - .5);
请问这样实现有没有问题?
回答:没有问题。
但是,如果你希望实现加密操作,例如生成密钥,尤其是在 Node.js 服务层,则 Math.random() 就有问题了,会有潜在的安全风险,需要使用 crypto.getRandomValues() 方法。
哦?安全风险?还有个 getRandomValues() 方法?
不急,我们慢慢聊。
二、Math.random的安全风险
提到 Math.random() 的安全风险,有开发人员会说因为 Math.random() 返回的是伪随机数。
这个解释似是而非,和伪随机数没有关系,getRandomValues() 方法返回的也是伪随机数。
还有人说因为 Math.random() 返回的随机值范围不是均匀的,这个回答就不是似是而非了,而是大错特错。
Math.random() 函数返回一个范围0-1的伪随机浮点数,其在 V8 中的实现原理是这样的:
为了保证足够的性能,Math.random() 随机数并不是实时生成的,而是直接生成一组随机数(64个),并放在缓存中。
当这一组随机数取完之后再重新生成一批,放在缓存中。
由于 Math.random() 的底层算法是公开的(xorshift128+ 算法),V8 源码可见,因此,是可以使用其他语言模拟的,这就导致,如果攻击者知道了当前随机生成器的状态,那就可以知道缓存中的所有随机数,那就很容易匹配与破解。
例如抽奖活动,使用 Math.random() 进行随机,那么就可以估算出一段时间内所有的中奖结果,从而带来非常严重且致命的损失。
此时应该使用 getRandomValues() 方法。