最近在扫描代码时Math.random报Make sure that using this pseudorandom number generator is safe,官方给出的解决方案是用crypto.getRandomValues。
那么,为什么Math.random会有安全风险呢?
Math.random()函数返回一个0~1之间的伪随机浮点数,其在V8中的实现原理是这样的:
为了保证足够的性能,Math.random()随机数并不是实时生成的,而是直接生成一组随机数(64个),并放在缓存中,当这一组随机数取完之后再重新生成一批,放在缓存中。
由于 Math.random() 的底层算法是公开的(xorshift128+ 算法),V8 源码可见,因此,是可以使用其他语言模拟的,这就导致,如果攻击者知道了当前随机生成器的状态,那就可以知道缓存中的所有随机数,那就很容易匹配与破解。
Crypto.getRandomValues() 和 Math.random() 方法的区别在于,getRandomValues() 方法的随机种子生成器更加的无序,例如系统层面的无序源(有些硬件自带随机种子)。
然后不同浏览器下 getRandomValues() 方法生成的随机数可能是有区别的。
以及 getRandomValues() 方法的底层实现是没有缓存的,随机数都是实时生成的,因此,性能上是要比 Math.random() 差的,因此,如果是高并发的场景,同时随机数仅仅是用做随机,与安全和金钱不相关,请使用 Math.random() 而不是 getRandomValues()。
就 Web 前端而言,必须要使用 getRandomValues() 方法的场景很少,不过由于纯前端几乎不存在所谓的高并发,因此使用 getRandomValues() 方法也是可以的。
Crypto.getRandomValues() 方法让你可以获取符合密码学要求的安全的随机值。传入参数的数组被随机值填充(在加密意义上的随机)。
为了确保足够的性能,不使用真正的随机数生成器,但是它们正在使用具有足够熵值伪随机数生成器。它所使用的 PRNG 的实现与其他不同,但适用于加密的用途。该实现还需要使用具有足够熵的种子,如系统级熵源。
cryptoObj.getRandomValues(typedArray);
typedArray是一个基于整数的 TypedArray,它可以是 Int8Array、Uint8Array、Int16Array、 Uint16Array、 Int32Array 或者 Uint32Array。在数组中的所有的元素会被随机数重写。(注释:生成的随机数储存在 typedArray 数组上。)
如果请求的长度超过 65536 字节,则抛出异常。
获取随机数:Window.crypto.getRandomValues(new Uint8Array(1))
Math.random()等价表达式:window.crypto.getRandomValues(new Uint8Array(1)) * 0.001
获得一个大于零的随机数:window.crypto.getRandomValues(new Uint32Array(1))[0];
获取 n个字母
let arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z'
];
let idvalue = ''
let n = 23; //这个值可以改变的,对应的生成多少个字母,根据自己需求所改
for (let i = 0; i < n; i++) {
const randomBuffer = new Uint32Array(1);
window.crypto.getRandomValues(randomBuffer);
let randomNumber = randomBuffer[0] / (0xffffffff + 1);
idvalue += arr[Math.floor(randomNumber * 26)]
}
console.log("随机数:"+idvalue+";长度:"+idvalue.length) //随机数:THIUDGYKOLVFXAWRYKHPLMF;长度:23
故 上面也可换为公共方法,来获取一定范围的随机数
function getRandomIntInclusive(min, max) {
const randomBuffer = new Uint32Array(1);
window.crypto.getRandomValues(randomBuffer);
let randomNumber = randomBuffer[0] / (0xffffffff + 1);
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(randomNumber * (max - min + 1)) + min;
}
let arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z'
];
let idvalue = ''
let n = 23; //这个值可以改变的,对应的生成多少个字母,根据自己需求所改
for (let i = 0; i < n; i++) {
idvalue += arr[getRandomIntInclusive(0, 25)]
}
参数是 typedArray 类型
类型范围如下:
Int8 - [-128 : 127]
Int16 - [-32768 : 32767]
Int32 - [-2147483648 : 2147483647]
Int64 - [-9223372036854775808 : 9223372036854775807]
UInt8 - [0 : 255]
UInt16 - [0 : 65535]
UInt32 - [0 : 4294967295]
UInt64 - [0 : 18446744073709551615]
可用subarray来截取,而不是substring、slice、substr
用到Math.round()、Math.ceil()、Math.floor()的原因是:输入的不一定都是整数