JavaScript随机数生成技术实践 | 为什么Math.random不是安全的随机算法?

技术原理概念解析

Math.random() 原理

Math.random() 函数返回一个范围在 0 - 1 的伪随机浮点数。为了保证足够的性能,它并不是实时生成随机数,而是直接生成一组 64 个随机数并放在缓存中。当这一组随机数取完之后,再重新生成一批放入缓存。其底层算法是公开的 xorshift128+ 算法,在 V8 源码中可见。

Crypto.getRandomValues() 原理

Crypto.getRandomValues() 方法同样返回伪随机数。它的随机种子生成器更加无序,例如会利用系统层面的无序源(有些硬件自带随机种子)。并且该方法的底层实现没有缓存,随机数都是实时生成的。

适用场景和边界条件

Math.random() 适用场景和边界条件

  • 适用场景 :在日常开发中,如果随机数仅用于普通的随机操作,如生成随机 ID、随机排序等,Math.random() 是一个不错的选择,因为它性能高且实用。例如生成随机 ID 的代码:document.body.id = ('_' + Math.random()).replace('0.', ''); ,随机排序的代码:[1, 2, 3, 4, 5].sort(_ => Math.random() - .5);
  • 边界条件 :如果随机值与加密相关,或者涉及到金钱等安全性要求非常高的场景,不能使用 Math.random() ,因为它存在安全风险。例如抽奖活动若使用 Math.random() 进行随机,攻击者有可能估算出一段时间内所有的中奖结果。

getRandomValues() 适用场景和边界条件

  • 适用场景 :当需要实现加密操作,例如生成密钥,尤其是在 Node.js 服务层,或者随机值与安全和金钱相关时,必须使用 getRandomValues() 方法。
  • 边界条件 :由于 getRandomValues() 方法性能比 Math.random() 差,在高并发的场景下,如果随机数仅仅是用做随机,与安全和金钱不相关,不建议使用该方法。在 Web 前端,纯前端几乎不存在高并发情况,使用该方法的性能优势不明显。另外,在 IE11 浏览器下需要添加 ms 私有前缀 window.msCrypto.getRandomValues() ,IE10 不支持该方法。

相关技术方案优缺点对比

技术方案 优点 缺点
Math.random() 性能高,使用方便,适合普通随机场景 存在安全风险,不适合安全要求高的场景
getRandomValues() 安全性高,适用于加密等安全要求高的场景 性能较差,方法名称长,记忆和使用不够敏捷

技术架构图/流程图

graph LR classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px; classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px; classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px; A([开始]):::startend --> B{随机场景类型?}:::decision B -->|普通随机| C[/使用 Math.random/]:::process B -->|安全要求高| D[/使用 getRandomValues/]:::process C --> E([结束]):::startend D --> E

最佳实践

准则一:普通随机场景使用 Math.random()

  • 原理说明Math.random() 函数返回一个范围在 0 - 1 的伪随机浮点数,它会预先生成一组 64 个随机数放在缓存中,取完后再重新生成一批。这种方式性能较高,适合普通随机场景,因为在这些场景下对随机数的安全性要求不高。
  • 实施步骤
    1. 在需要生成随机数的代码位置调用 Math.random() 函数。
    2. 根据具体需求对生成的随机数进行处理,例如生成随机 ID 可以将其转换为字符串并拼接;进行随机排序可以将其作为排序函数的比较参数。示例代码如下:
javascript 复制代码
// 生成随机 ID
const randomId = ('_' + Math.random()).replace('0.', '');
console.log(randomId);

// 随机排序
const array = [1, 2, 3, 4, 5];
const sortedArray = array.sort(() => Math.random() - 0.5);
console.log(sortedArray);
  • 常见误区警示 :不要将 Math.random() 用于与加密相关或涉及金钱等安全性要求非常高的场景,因为它存在安全风险,攻击者有可能估算出一段时间内的随机结果。
  • 性能指标参考值Math.random() 的性能较高,在普通随机场景下,其响应时间通常在毫秒级别,每秒可以生成大量的随机数。

准则二:安全要求高的场景使用 getRandomValues()

  • 原理说明Crypto.getRandomValues() 方法的随机种子生成器更加无序,会利用系统层面的无序源,并且随机数是实时生成的,没有缓存。这使得它生成的随机数安全性更高,适合加密等安全要求高的场景。
  • 实施步骤
    1. 创建一个 Uint8ArrayUint16ArrayUint32Array 类型的数组,用于存储生成的随机数。
    2. 调用 Crypto.getRandomValues() 方法,并将数组作为参数传入。示例代码如下:
javascript 复制代码
// 创建一个长度为 16 的 Uint8Array 数组
const array = new Uint8Array(16);
// 生成随机数并存储在数组中
window.crypto.getRandomValues(array);
console.log(array);
  • 常见误区警示 :由于 getRandomValues() 方法性能比 Math.random() 差,在高并发的普通随机场景下不建议使用。另外,在 IE11 浏览器下需要添加 ms 私有前缀 window.msCrypto.getRandomValues() ,IE10 不支持该方法。
  • 性能指标参考值getRandomValues() 的性能相对较低,生成随机数的响应时间会比 Math.random() 长,每秒生成的随机数数量也会相对较少。在实际应用中,需要根据具体的性能要求进行评估。

代码实验室

基础实现案例:使用 Math.random() 生成随机整数

  • 代码块
javascript 复制代码
// 此代码使用 JavaScript 编写,版本要求为 ES6 及以上
// 定义一个函数,用于生成指定范围内的随机整数
function getRandomInt(min, max) {
    // Math.random() 生成一个 0(包含)到 1(不包含)之间的随机浮点数
    // 通过乘以 (max - min + 1) 并加上 min,将范围调整到 [min, max]
    // Math.floor() 向下取整,确保结果为整数
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

// 调用函数,生成 1 到 10 之间的随机整数
const randomInt = getRandomInt(1, 10);
console.log(randomInt);
  • 环境要求说明:此代码可以在任何支持 JavaScript 的环境中运行,如浏览器的开发者工具控制台、Node.js 环境等。确保你的环境支持 ES6 及以上版本的 JavaScript。
  • 预期输出示例 :控制台将输出一个 1 到 10 之间的随机整数,例如:5
  • 异常处理指南 :如果传入的 minmax 不是有效的数字,可能会导致结果不符合预期。在调用函数时,确保传入的参数是有效的数字。

生产级优化方案案例:使用 getRandomValues() 生成安全的随机密码

  • 代码块
javascript 复制代码
// 此代码使用 JavaScript 编写,版本要求为 ES6 及以上
// 定义一个函数,用于生成指定长度的安全随机密码
function generateSecurePassword(length) {
    // 创建一个 Uint8Array 数组,用于存储随机数
    const array = new Uint8Array(length);
    // 使用 Crypto.getRandomValues() 生成随机数并存储在数组中
    window.crypto.getRandomValues(array);
    // 定义密码可能包含的字符集
    const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=';
    let password = '';
    // 遍历数组,将每个随机数映射到字符集中的一个字符
    for (let i = 0; i < length; i++) {
        password += characters[array[i] % characters.length];
    }
    return password;
}

// 调用函数,生成一个长度为 16 的安全随机密码
const securePassword = generateSecurePassword(16);
console.log(securePassword);
  • 环境要求说明 :此代码需要在支持 Crypto.getRandomValues() 方法的环境中运行,如现代浏览器或 Node.js 环境。在 IE11 浏览器下需要添加 ms 私有前缀 window.msCrypto.getRandomValues() ,IE10 不支持该方法。
  • 预期输出示例 :控制台将输出一个长度为 16 的安全随机密码,例如:aB3@dE5#fG7*hI9$
  • 异常处理指南 :如果浏览器不支持 Crypto.getRandomValues() 方法,代码将抛出错误。在使用该方法之前,可以先检查浏览器是否支持,例如:if ('crypto' in window && 'getRandomValues' in window.crypto) { /* 执行代码 */ }

延伸学习

为了帮助开发者更深入地学习 JavaScript 中 Math.random()Crypto.getRandomValues() 生成随机数的相关技术,以下是一些权威的学习资源推荐:

官方文档章节

  • MDN Web Docs:Math.random():MDN 是全球开发者共同维护的 Web 技术文档平台,提供了关于 Math.random() 的详细文档,包括语法、返回值、示例代码以及规范等内容。通过阅读该文档,开发者可以了解 Math.random() 的基本用法和适用场景,同时也能了解到该方法的局限性,如不能提供密码学安全的随机数。
  • MDN Web Docs:Crypto.getRandomValues():同样来自 MDN Web Docs,该文档详细介绍了 Crypto.getRandomValues() 方法。包括该方法的原理、参数、异常处理以及示例代码等。对于需要生成加密强度随机数的开发者来说,这是一个非常重要的参考文档。

经典论文/技术报告

高质量开源项目

  • Seedrandom:这是一个由 David Bau 开发的小巧但功能强大的 JavaScript 库,它允许程序员创建可重复的随机数序列。在许多场景中,能够控制随机性的能力是非常宝贵的,例如在测试、游戏开发或加密应用中。Seedrandom 提供了一个 API,通过种子(seed)生成随机数,给定相同的种子,它会返回完全一样的随机数序列。
  • react-native-get-random-values:这是一个适用于 React Native 平台的开源项目,是对 Web 标准 crypto.getRandomValues 的精巧实现。该项目弥补了 React Native 环境中默认未完全集成 crypto.getRandomValues 功能的空白,使得诸如 uuid 这样的流行库能在 React Native 应用中无缝工作,保证生成唯一标识符等场景下的数据安全性。

进阶技术路线图

  • 在掌握了 Math.random()Crypto.getRandomValues() 的基本用法后,你可以进一步学习 Web Crypto API 的其他功能,如加密、解密、签名和验证等。可以参考 MDN Web Docs 上的 Web Cryptography API 文档进行深入学习。
  • 对于需要在 Node.js 环境中使用加密功能的,可以学习 Node.js 的 crypto 模块。可以参考 Node.js v23 文档:crypto 加密 了解该模块的详细用法。
相关推荐
web守墓人2 小时前
【gpt生成-其一】以go语言为例,详细描述一下 :语法规范BNF/EBNF形式化描述
前端·gpt·golang
互联网搬砖老肖4 小时前
Selenium2+Python自动化:利用JS解决click失效问题
javascript·python·自动化
pink大呲花4 小时前
使用 Axios 进行 API 请求与接口封装:打造高效稳定的前端数据交互
前端·vue.js·交互
samuel9184 小时前
uniapp通过uni.addInterceptor实现路由拦截
前端·javascript·uni-app
benben0444 小时前
Unity3D仿星露谷物语开发35之锄地动画
前端·游戏·游戏引擎
WebInfra5 小时前
🔥 Midscene 重磅更新:支持 AI 驱动的 Android 自动化
android·前端·测试
八了个戒5 小时前
「数据可视化 D3系列」入门第八章:动画效果详解(让图表动起来)
开发语言·前端·javascript·数据可视化
八了个戒5 小时前
「数据可视化 D3系列」入门第九章:交互式操作详解
javascript·信息可视化·数据可视化·d3
拉不动的猪6 小时前
无缝适配 PC 和移动端‌我们要注意哪些点呢
前端·javascript·面试