极简工具箱

免费在线工具 · 本地计算 · 无需上传

随机数怎么生成才安全:伪随机、种子与验证码

`Math.random()` 看起来够随机了,但它的输出可以被预测;用它生成验证码或抽奖,等于把结果提前告诉别人。

广告位(百度联盟)

一、伪随机不是「假随机」

计算机里的随机数通常是伪随机:由一个确定的算法(如 Mersenne Twister、PCG)根据「种子」生成序列。特点:

「可复现」在调试和实验里是优点(同样的种子能重现问题),但在安全场景是致命缺点。

二、种子

import random
random.seed(42)
print(random.random())   # 每次都一样

不设种子时,多数语言用当前时间或系统熵初始化,所以每次运行不同。但只要种子可猜,序列就可预测。

有些老系统的漏洞就来自「用时间戳做种子」:攻击者知道大致时间,就能枚举出种子,进而预测出「随机」的 token。

三、安全场景要用密码学随机

// 不安全:不要用于 token、验证码、密码
Math.random()

// 安全
crypto.randomUUID()
crypto.getRandomValues(new Uint8Array(32))
import secrets
secrets.token_hex(16)
secrets.choice('abcdefghijklmnopqrstuvwxyz')

判断标准:这个随机数会不会影响安全(身份、金额、权限、抽奖结果)?会,就用 secrets / crypto 这类密码学安全的接口。

Math.random() 的实现(V8 用 xorshift128+)状态可以被反推,观察到足够多的输出就能预测后续值。

四、均匀分布与取模偏差

想生成 [0, 9] 的随机整数,直接 random() * 10 取整会有极小的不均匀;用「随机数 % n」在大范围内也有模偏差(当范围不是 n 的整数倍时,部分值概率略高)。

正确做法是用库提供的 randint(a, b) 或 secrets.randbelow(n),它们内部做了拒绝采样,保证均匀。

抽奖场景对公平性敏感,模偏差虽然极小,但应该用标准接口避免争议。

五、验证码与 token

随机性只是第一层,限流、过期、一次性才是防爆破的关键。

六、可复现随机

需要「同样输入得到同样结果」时(如分桶实验、生成固定测试数据、游戏地图),用固定种子:

rng = random.Random(20261103)
group = rng.choice(['A', 'B'])

或者用哈希做确定性分桶:

bucket = hash(user_id + salt) % 100

这比随机分配更好:同一个用户每次进来都落在同一组,不会因为刷新而切组。

七、几个误区

八、工具

用 随机生成器 生成随机数、随机字符串、抽签;生成强密码用 密码生成器;生成唯一标识用 UUID 生成器。这些工具都在浏览器本地运行,生成结果不会上传。

小结

伪随机由种子决定、可复现;安全场景用 secrets / crypto,不用 Math.random;取模有偏差,用标准 randint;验证码靠限流与过期而不是随机性;分桶用哈希而不是每次随机。

本文相关工具(本地计算,无需上传)

更多使用教程