找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3187

积分

0

好友

443

主题
发表于 13 小时前 | 查看: 2| 回复: 0

当我们需要在程序中生成一个随机数时,Math.random() 往往是开发者的首选。它简单、直接,一行代码就能得到一个 0 到 1 之间的浮点数。

然而,这个看似万能的工具,却藏着一个关乎安全的致命缺陷。

Math.random() 的“原罪”:它是可预测的

Math.random() 生成的数字并非真正的随机,而是伪随机

什么是伪随机?它是由一个确定的算法,根据一个初始值(称为“种子”)计算出来的一系列数字。这个算法本身是公开的,这意味着,如果你知道了初始的“种子”,你就能完全预测出接下来生成的每一个“随机数”。

在早期的浏览器中,这个“种子”甚至可能只是简单的时间戳,使得预测变得非常容易。虽然现代浏览器已经改进了种子的生成方式,使其更难被猜测,但 Math.random() 的核心机制并没有改变。ECMAScript 规范本身不要求 Math.random() 必须是密码学安全的。这意味着,对于涉及令牌生成、抽奖或加密等场景,使用它是一个安全隐患。

更安全的替代方案:crypto.getRandomValues()

window.crypto 是浏览器提供的一套用于密码学操作的 API,而 crypto.getRandomValues() 就是其中的一员。它是一个密码学安全伪随机数生成器 (CSPRNG)

Math.random() 不同,crypto.getRandomValues() 的设计目标就是提供密码学级别的安全性

它是如何做到“真正随机”的?

它直接从操作系统底层获取高质量的“熵 (Entropy)”。这些熵的来源是不可预测的物理事件,例如:

  • 鼠标移动的精确时机和轨迹
  • 键盘输入的时机
  • 硬件设备产生的微小噪声
  • 网络数据包的到达时间

操作系统将这些不可预测的事件混合成一个“熵池”,crypto.getRandomValues() 正是从这个池中获取随机性,使其生成的数值在统计学上是真正不可预测的。

如何使用 crypto.getRandomValues()

它的用法与 Math.random() 有所不同。它不是直接返回一个数字,而是用于填充一个类型化数组 (Typed Array),如 Uint8ArrayUint32Array

基础用法:

// 创建一个包含 10 个字节的数组
const randomBytes = new Uint8Array(10);

// 用密码学安全的随机值填充它
crypto.getRandomValues(randomBytes);

console.log(randomBytes); // 输出: Uint8Array(10) [185, 20, 248, 119, ...]

这看起来似乎没那么直观,但别担心,我们可以轻松地将它封装成我们习惯使用的函数。

替代 Math.random() 的函数:
我们可以生成一个 32 位无符号整数,然后将其转换为 0 到 1 之间的浮点数。

使用 crypto.getRandomValues 生成安全随机浮点数的JavaScript代码示例

生成范围内安全随机整数的函数(常用):

function secureRandomInt(min, max) {
 const range = max - min + 1;
 // 创建一个足够大的随机数,以减少模偏差
 const randomValue = new Uint32Array(1);
  crypto.getRandomValues(randomValue);

 return min + (randomValue[0] % range);
}

console.log(secureRandomInt(1, 6));   // 模拟安全的骰子
console.log(secureRandomInt(1000, 9999)); // 生成一个安全的 4 位验证码

如何选择:安全场景 vs. 非安全场景

明白了原理和用法,关键在于如何选择。Math.random() 适用于那些不涉及安全或公平性的纯视觉效果或特定玩法场景,例如:

  • 生成随机的粒子效果、模拟下雨或下雪
  • 创作随机的图案和视觉效果
  • 需要玩家通过分享种子来玩到完全相同的游戏关卡

而当你的应用涉及以下安全敏感场景时,请毫不犹豫地选择 crypto.getRandomValues()

  • 生成用户会话令牌 (Token) 或 CSRF 令牌
  • 创建加密密钥或初始化向量 (IV)
  • 进行抽奖、彩票等需要保证绝对公平的随机分配
  • 生成高安全性的验证码或一次性密码 (OTP)

目前,crypto.getRandomValues() API 早已兼容各现代浏览器(IE 除外),完全可以投入生产环境使用。在开源社区和技术论坛的讨论中,关于前端安全的实践也越来越受到重视。将默认的 Math.random() 替换为更安全的随机源,正是构建健壮Web应用的一个细小但重要的步骤。希望本文能帮助你在合适的场景做出正确的技术选择,也欢迎到云栈社区的前端板块交流更多关于 Web API 和开发安全的实践。




上一篇:终于等到这一天:经验丰富的程序员,正被AI编程‘续命’
下一篇:Vue3项目中如何简化WebSocket开发?vue-native-websocket-vue3插件实战解析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-10 18:27 , Processed in 0.399974 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表