介绍
在几个用例中,程序可能需要安全的随机数生成源。通常,我们需要随机数用于游戏功能,例如骰子或抽奖、私钥生成或其他需要加密安全源的类似程序。
在 JavaScript 中,我们在Math
对象中有random()方法的本地实现,这是一个内置对象,具有用于执行数学计算的属性或方法。顾名思义,该random()
方法帮助我们生成随机数。
该Math.random()
方法返回一个介于零(含)和一(不含)之间的十进制数或浮点伪随机数。在数学术语中,这表示为0 >= x < 1
。
虽然有多种方法可以使用此Math.random()
方法在特定范围内产生随机结果,但它并不是真正的随机数生成器。这是因为它是伪随机的;随着时间的推移,数字将开始重复并最终显示出非随机模式。你可能知道,计算机很难生成真正的随机数。
然而,虽然在大多数情况下Math.random()
生成的伪随机数通常是足够的,但有时我们需要一个密码安全的随机数生成源。我的意思是我们希望随机数不容易通过模式猜测,或者随着时间的推移最终会重复。
JavaScript和Node.js如何构建随机数生成器?在本文中,我们将重点介绍针对这些情况的推荐方法。但在此之前,让我们检查一下该Math.random()
方法的一些基本用例,以便我们学习在 JavaScript 中生成随机数的最简单方法。
Node随机数生成器构建教程:JavaScript 中方法的用例Math.random()
Math.random()
返回一个非加密随机数,它从称为“种子”的隐藏内部表示开始。种子表示在指定范围内均匀生成的隐藏数字序列的起点。下面解释了此方法的一些用例。
该Math.random()
方法的第一个(也是最简单的)用例是生成零到一之间的随机十进制或浮点数:
const randomNumber = Math.random()
console.log(randomNumber) outputs -> //0.8446144685704831
请注意,通过生成 0 和 1 之间的随机数,我们可以通过将随机数与另一个数字相乘来将结果缩放到我们需要的任何大小。
Node随机数生成器实例:
const max = 6
const randomNumber = Math.floor(Math.random() * max)
console.log(randomNumber) outputs -> 0,1,2,3,4,5
此方法的另一个用例是使用Maths
对象中的另一个方法在两个特定整数范围之间生成一个随机整数floor
。该floor
方法返回小于或等于指定数字的最大整数。
我们可以在指定范围内生成一个浮点数,如下例所示:
// generating floating point numbers within a range
const max = 4
const min= 2
const result = Math.random()*(max - min)
console.log(result) //1.4597999233994834
// generating random integers within a range
const max = 4
const min= 2
const result = Math.random()*(max - min) + min
console.log(Math.floor(result))//3
该Math.random()
函数的其他用例包括在具有最大限制的特定指定范围之间生成随机数。在这里,我们可以使用ceil
,Maths
对象中的另一种方法。
现在,在下一节中,让我们探讨使用这些伪随机数生成器的一些缺点。
安全隐患 Math.random()
Math.random()
在安全性方面有几个缺点。根据 MDN 文档,Math.random()
不保证随机数加密安全。因此,建议不要将它们用于我们程序中与安全相关的任何事情。
这些安全故障部分是由于以下原因:
- 在均匀分布内生成随机整数时采用的逻辑不充分且通常有偏差
- 浏览器关于使用多少位/字节的随机性的不一致
- 随机结果总是难以一致地重放,使其本质上具有不确定性和不规则性
- 内置种子可以被篡改,使其在完整性方面不合适
由于这些漏洞,万维网联盟提出了Web Crypto API 的实现。在撰写本文时,所有流行的浏览器都通过crypto
对象向 JavaScript 应用程序提供了此 API 的实现。
让我们快速了解一下 Web Crypto API 以及如何使用它。
Web 加密 API 简介
Web Crypto API 提供了许多可以通过Window.crypto
属性访问的加密方法和函数。在浏览器中,我们可以使用该crypto.getRandomValues(Int32Array)
方法,该方法承诺以加密方式生成随机数。
在服务器端,Node.js 还提供了标准 Web Crypto API 的实现。为了使用这个模块,我们可以用require('crypto').randomBytes(size)
初始化它,因为 crypto 包是 Node.js 原生的。
Web Crypto API 中使用的伪随机数生成器算法 (PRNG) 可能因不同的浏览器客户端而异。然而,它适用于大多数密码学目的,只要内部种子有足够的熵,可能来自外部源,如 Unix /dev/urandom
。
在下一节中,我们将讨论如何使用 Web Crypto API,包括语法、我们可以传递的参数和返回值。下面先从基本用法开始。
Node随机数生成器构建教程:使用 Web 加密 API
该Crypto.getRandomValues()
方法让我们获得加密强随机值。 本质上,该Crypto
接口代表了一种通用的加密功能。
它在大多数 Web 浏览器中可用,尽管实现可能会有所不同,但它们都需要使用具有足够熵的种子。这是为了确保不会对性能和安全产生负面影响。
该getRandomValues()
方法是唯一可以在不安全上下文中使用的Crypto
接口成员。因此,在生成加密密钥时不建议使用,因为它们不能保证返回安全的结果。在这种情况下,我们可以使用该generateKey()
方法。
现在,让我们看看getRandomValues
Web 加密 API的方法的语法、接受的参数和返回值。
句法
Web Cryptography API 接受ArrayBuffer
类的实例并TypedArray
作为输入来表示字节序列。
请参阅以下语法:
typedArray = cryptoObj.getRandomValues(typedArray);
参数
typedArray
是一个基于整数的TypedArray
对象。它可以是 Int8Array
、 Uint8Array
、 Int16Array
、Uint16Array
、Int32Array
或 Uint32Array
。
请注意,数组中的所有元素都填充了随机数。
返回值
返回值是作为typedArray
传入的同一个数组,但其内容替换为新生成的随机数。
在下一节中,我们将研究如何编写一个简单的程序来生成安全的加密随机数。
生成随机数
JavaScript和Node.js如何构建随机数生成器?出于安全目的(即,存在攻击者可能性的任何地方)所需的每个随机值都应使用加密安全伪随机数生成器(也称为 CSPRNG)生成。
这包括验证或重置令牌、彩票号码、API 密钥、生成的密码、加密密钥等。
那么,我们如何生成安全可靠的随机数呢?最好的选择是采用一个通过设计来处理这些安全问题的库。在 Node 中,我们有几个选项:
- 为了在一个范围内生成随机数,有使用 Crypto API 引擎盖下的random-number-csprng
- 对于 API 密钥或令牌,有uuid,特别是函数
uuid.v4()
让我们看下面的Node随机数生成器实例。
启动一个简单的 Node 应用程序以采用 npm 包中最简单的示例:
var Promise = require("bluebird");
var randomNumber = require("random-number-csprng");
Promise.try(function() {
return randomNumber(10, 30);
}).then(function(number) {
console.log("Your random number:", number);
}).catch({code: "RandomGenerationError"}, function(err) {
console.log("Something went wrong!");
});
Output shown below:
retina@alexander random-number-generator % node index.js
Your random number: 24
retina@alexander random-number-generator % node index.js
Your random number: 14
retina@alexander random-number-generator % node index.js
Your random number: 20
retina@alexander random-number-generator % node index.js
Your random number: 21
retina@alexander random-number-generator % node index.js
Your random number: 11
retina@alexander random-number-generator % node index.js
Your random number: 26
retina@alexander random-number-generator % node index.js
Your random number: 23
retina@alexander random-number-generator % node index.js
Your random number: 15
retina@alexander random-number-generator % node index.js
Your random number: 22
retina@alexander random-number-generator % node index.js
Your random number: 28
如上例所示,我们可以在一个范围内生成加密安全的伪随机数。该randomNumber
方法返回一个承诺,该承诺解析为指定范围内的随机数。可以在GitHub 存储库 API 部分找到更多详细信息。
在 Node.js 中使用 Web Crypto API
Node.js 提供了标准 Web Crypto API 的实现,尽管在撰写本文时,它仍然是该语言的实验性功能。
我们可以通过调用require('crypto').webcryptoCrypto
在 Node 中使用这个模块。这将返回该类的一个实例,该实例提供对加密 API 其余部分的访问。
正如我们之前所讨论的,
crypto.getRandomValues(typedArray)
生成加密强随机值。有关 Node 中 Web 加密 API 的更多详细信息,请参阅Node 文档。
Node随机数生成器构建教程结论
JavaScript和Node.js如何构建随机数生成器?没有模式或算法适用的真正随机性是否真的存在是有争议的。然而,对于与安全相关的代码,我们需要一个攻击者无法预测的随机数。在这种情况下,数据是如何生成的并不重要,只要不能被猜到就行。
为此,万维网联盟发布了 Web Cryptography API,它允许浏览器中的 JavaScript 应用程序使用常见的加密功能,而无需使用任何第三方库。
正如我们之前提到的,这个 API 的crypto.getRandomValues方法是 Web 应用程序获取加密安全随机数据的最安全的方式。
Web Crypto API 的所有其他功能都可以通过crypto.subtle对象访问。可以在此处找到 Web 加密标准的链接。
感谢你的阅读,请不要犹豫,在下面的部分中留下你的问题或评论。