深入JS getRandomValue和Math.random方法

更新日期: 2021-12-21阅读: 1.6k标签: Math

一、引言

我们日常开发经常会用到随机数,基本上我接触下来,都是使用 Math.random() 生成的。

例如生成随机ID:

document.body.id = ('_' + Math.random()).replace('0.', '');

请问这样实现有没有问题?

回答:没有问题。

例如随机排序:

[1, 2, 3, 4, 5].sort(_ => Math.random() - .5);

请问这样实现有没有问题?

回答:没有问题。

但是,如果你希望实现加密操作,例如生成密钥,尤其是在 Node.js 服务层,则 Math.random() 就有问题了,会有潜在的安全风险,需要使用 crypto.getRandomValue() 方法。

哦?安全风险?还有个 getRandomValue() 方法?

不急,我们慢慢聊。

二、Math.random的安全风险

提到 Math.random() 的安全风险,有开发人员会说因为 Math.random() 返回的是伪随机数。

这个解释似是而非,和伪随机数没有关系, getRandomValue() 方法返回的也是伪随机数。

还有人说因为 Math.random() 返回的随机值范围不是均匀的,这个回答就不是似是而非了,而是大错特错。

例如我运行个2万次 Math.random() 方法的分布图是下面这样的(实时Canvas绘制,外站无效果,点击重新绘制):

0 0.5 1

可以看到从左到右基本上是均匀的。

那究竟为何是不安全的呢?

这个就要讲讲 Math.random() 方法的底层实现了,这里有 一篇文章 有深入介绍,我简述下其中的要点。

Math.random() 函数返回一个范围0-1的伪随机浮点数,其在 V8 中的实现原理是这样的:

为了保证足够的性能, Math.random() 随机数并不是实时生成的,而是直接生成一组随机数(64个),并放在缓存中。

当这一组随机数取完之后再重新生成一批,放在缓存中。

由于 Math.random() 的底层算法是公开的(xorshift128+ 算法),V8 源码可见,因此,是可以使用其他语言模拟的,这就导致,如果攻击者知道了当前随机生成器的状态,那就可以知道缓存中的所有随机数,那就很容易匹配与破解。

例如抽奖活动,使用 Math.random() 进行随机,那么就可以估算出一段时间内所有的中奖结果,从而带来非常严重且致命的损失。

此时应该使用 getRandomValue() 方法。

//zxx: 如果你看到这段文字,说明你现在访问是不是原文站点,更好的阅读体验在这里:https://www.zhangxinxu.com/wordpress/?p=10241(作者张鑫旭)

三、了解getRandomValue方法

Crypto.getRandomValues() 方法返回的也是伪随机数,不是真随机,按照 MDN 的说法,是为了性能考虑,没有使用真随机。

实际上,按照我的认识,所有可以使用算法生成的随机数都可以看成是伪随机数,真随机数应该是存在自然界,例如粒子的起伏,声音的噪点,分子的分布等。

约翰·冯·诺伊曼(玩笑话):任何使用算术方法生成随机数的人,都是有罪的

和 Math.random() 方法的区别在于, getRandomValues() 方法的随机种子生成器更加的无序,例如系统层面的无序源(有些硬件自带随机种子)。

然后不同浏览器下 getRandomValues() 方法生成的随机数可能是有区别的。

以及 getRandomValues() 方法的底层实现是没有缓存的,随机数都是实时生成的,因此,性能上是要比 Math.random() 差的,因此,如果是高并发的场景,同时随机数仅仅是用做随机,与安全和金钱不相关,请使用 Math.random() 而不是 getRandomValues() 。

就 Web 前端而言,必须要使用 getRandomValues() 方法的场景很少,不过由于纯前端几乎不存在所谓的高并发,因此,你使用 getRandomValues() 方法也是可以的,有装逼的作用。

语法和使用

使用示意(下面代码 self 不太了解可以参见此文“了解全局作用域self”):

let randNumber = self.crypto.getRandomValues(new Uint32Array(1))[0];
// 一串随机整数,通常10位
console.log(randNumber);

语法为:

crypto.getRandomValues(typedArray)

支持的参数 typedArray 表示整数型的类型数组,包括:Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array 或者 Uint32Array。

返回值回是所有被替换为随机数的新的数组。

不过 getRandomValues() 方法名称有些长,不利于记忆和敏捷使用,我们可以改造下,例如:

Math.randomValue = function () {
    return self.crypto.getRandomValues(new Uint32Array(1))[0];
};

这样我们就可以使用 Math.randomValue() 方法返回足够安全的随机值了。

IE浏览器

在 IE11 浏览器下需要添加 ms 私有前缀:

window.msCrypto.getRandomValues()

IE10 不支持 getRandomValues() 方法。

四、Crypto对象与其他随机值

UUID生成

除了生成随机数,Crypto对象还可以用来生成字符长度为36的 UUID (Universally Unique Identifier的缩写,表示唯一通用标识符)。

例如:

let uuid = self.crypto.randomUUID();
console.log(uuid);
// 示意输出:2433df46-d77f-4eb9-bbdd-4cd99361fe08


不过这个 api 是今年才支持的(Chrome 92+),还比较新,大家先了解下,等明年这个时候,差不多可以在一些前沿项目中尝试使用了。

crypto.subtle对象

crypto 对象还支持一个名为 subtle 的属性,目前唯一的属性,返回的属性值是一个对象,称为 SubtleCrypto 对象,包含大量的方法,可以用来生成各种签名和密钥,所有方法均返回 Promise,包括:

  • SubtleCrypto.encrypt()
  • SubtleCrypto.decrypt()
  • SubtleCrypto.sign()
  • SubtleCrypto.verify()
  • SubtleCrypto.digest()
  • SubtleCrypto.generateKey()
  • SubtleCrypto.deriveKey()
  • SubtleCrypto.deriveBits()
  • SubtleCrypto.importKey()
  • SubtleCrypto.exportKey()
  • SubtleCrypto.wrapKey()
  • SubtleCrypto.unwrapKey()

由于这些方法非本文重点,以及我们日常开发很少会用到,所以不展开介绍,有兴趣可以去 MDN 文档 查看。

五、结语

我们日常开发开始推荐使用 Math.random() 方法,高性能且实用,但是如果我们的随机值与加密相关,或者涉及到金钱等安全性要求非常高的场景,务必使用 getRandomValue() 方法。

原文来自:https://www.zhangxinxu.com/wordpress/2021/12/js-getrandomvalue-math-random/

链接: https://fly63.com/article/detial/10954

JavaScript Math对象中Math.round()四舍五入

s中Math.round()是一个数值实现“四舍五入”的方法,在msdn和w3school文档中是理解为:把一个数字舍入为最接近的整数。在我们实际应用中:math.round(-11.6)的结果为-12这个好理解,但是math.round(-11.5)返回值为什么是-11而不是-12呢?

JS实现使用Math.random()函数生成n到m间的随机数字

Math.random()函数返回0和1之间的伪随机数,可能为0,但总是小于1,[0,1);生成n-m,包含n但不包含m的整数: 第一步算出 m-n的值,假设等于w ,第二步Math.random()*w

Js Math对象 abs() 方法

语法Math.abs(x) // 必需。必须是一个数值。示例,在项目中使用的方法 上传图片;关键代码

Js随机Math.random()

Math.random() 返回 0(包括) 至 1(不包括) 之间的随机数:Math.random() 与 Math.floor() 一起使用用于返回随机整数。正如你从上面的例子看到的,创建一个随机函数用于生成所有随机整数是一个好主意。

Javascript中Math.max和Math.max.apply的区别和用法

最近在做一个小案例的时候遇到了Math.max.apply这么一个用法,之前很少遇到过感觉挺有趣的,就记录一下。

Js中Math常用方法整理

JavaScript中的math 对让我们能够对执行一些数学操作。 它具有数学常数和函数的属性和方法。 在今天的文章中将介绍 Math对象的一些有用方法。Math.min()是 JS 数学库中的函数,用于将所有传递的值中的最小值返回给该方法。

为什么不带参数的 Math.max() 返回-Infinity

Math.max() 是 JS 内置的方法,可以从传入的参数中,返回最大的一个。如果Math.max()只使用一个参数,结果是怎么样的?正如预期的那样,一个数字的最大值就是它本身。

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!