2

深入JS getRandomValue和Math.random方法

 2 years ago
source link: https://www.zhangxinxu.com/wordpress/2021/12/js-getrandomvalue-math-random/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

深入JS getRandomValue和Math.random方法

这篇文章发布于 2021年12月20日,星期一,23:10,归类于 JS API。 阅读 651 次, 今日 457 次 没有评论

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=10241 鑫空间-鑫生活
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。

随机与安全JS

我们日常开发经常会用到随机数,基本上我接触下来,都是使用 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绘制,外站无效果,点击重新绘制):

00.51

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

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

这个就要讲讲 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

uuid运行结果截图

不过这个 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() 方法。

好,以上就是本文的全部内容。

是不是学到了不少知识呢,那还等什么,赶快给你的小伙伴吧。

1f913.svg

(本篇完)1f44d.svg 是不是学到了很多?可以分享到微信
1f44a.svg 有话要说?点击这里


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK