Python 随机数:random 模块与 secrets 模块
Python 提供了两种生成随机数的方式:random 模块和 secrets 模块。它们用途不同,安全性也不同。不要混淆使用场景——日常模拟用 random,密码、令牌等安全敏感数据必须用 secrets。
1. random 模块:用于模拟和非安全场景
random 模块基于伪随机数生成器(PRNG),速度快但可预测。只要知道初始种子(seed),就能复现整个随机序列。因此它适用于游戏、抽样、仿真等不需要保密的场合。
基本用法
导入 random 模块:
import random
生成 0 到 1 之间的浮点数:
x = random.random() # 返回 [0.0, 1.0) 范围内的数
生成指定范围的整数:
n = random.randint(1, 10) # 返回 1 到 10(含)之间的整数
从列表中随机选择一个元素:
items = ['apple', 'banana', 'cherry']
choice = random.choice(items)
打乱列表顺序(原地修改):
deck = [1, 2, 3, 4, 5]
random.shuffle(deck) # deck 变为随机顺序
设置种子以复现结果:
random.seed(42) # 设置固定种子
print(random.random()) # 每次运行都输出相同的数
⚠️ 警告:
random模块生成的数不能用于加密、密码、安全令牌等场景。攻击者可通过观察部分输出推测内部状态,进而预测后续值。
2. secrets 模块:用于安全敏感场景
secrets 模块从操作系统提供的加密安全随机源(如 /dev/urandom 或 Windows CryptoAPI)获取随机数,不可预测且抗攻击。适用于生成密码、API 密钥、一次性令牌等。
基本用法
导入 secrets 模块(Python 3.6+ 内置):
import secrets
生成安全的随机整数:
n = secrets.randbelow(100) # 返回 [0, 100) 范围内的整数
生成指定比特长度的随机整数:
k = secrets.randbits(128) # 返回一个 128 位的随机整数
从序列中安全地选择一个元素:
choices = ['admin', 'user', 'guest']
role = secrets.choice(choices)
生成 URL 安全的随机令牌:
token = secrets.token_urlsafe(32) # 生成 32 字节的 Base64 编码字符串,适合用作会话 ID
生成十六进制格式的随机字符串:
hex_token = secrets.token_hex(16) # 生成 32 字符长的十六进制字符串(16 字节)
✅ 最佳实践:只要涉及用户凭证、重置链接、API 密钥等,一律使用
secrets。
3. 关键区别对比
以下表格总结了两个模块的核心差异:
| 特性 | random 模块 |
secrets 模块 |
|---|---|---|
| 随机源 | 伪随机算法(Mersenne Twister) | 操作系统加密安全随机源 |
| 可预测性 | 是(已知种子可复现) | 否(不可预测) |
| 性能 | 快 | 稍慢(但对多数应用无感) |
| 适用场景 | 模拟、游戏、抽样 | 密码、令牌、密钥、安全 ID |
| 是否支持设置种子 | 是 | 否 |
| Python 最低版本 | 所有版本 | 3.6+ |
4. 常见错误与正确做法
错误:用 random 生成密码重置链接
# ❌ 危险!不要这样做
import random
reset_code = ''.join(random.choices('0123456789', k=6))
攻击者可能通过多次请求分析模式,甚至暴力破解。
正确:用 secrets 生成安全令牌
# ✅ 安全做法
import secrets
reset_token = secrets.token_urlsafe(32)
该令牌足够随机,无法被预测或碰撞。
错误:用 random.shuffle 打乱敏感数据顺序并认为“安全”
即使顺序被打乱,若攻击者知道算法和部分输入,仍可能逆推。
正确:仅在非安全上下文中使用 random.shuffle
例如洗牌游戏可以,但用户权限列表的随机分配若涉及安全策略,则应避免依赖随机性做访问控制。
5. 实用示例:生成强密码
使用 secrets 和 string 模块组合生成符合安全要求的密码:
import secrets
import string
def generate_password(length=12):
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
while True:
password = ''.join(secrets.choice(alphabet) for _ in range(length))
# 确保至少包含一个小写字母、大写字母、数字和特殊字符
if (any(c.islower() for c in password)
and any(c.isupper() for c in password)
and any(c.isdigit() for c in password)
and any(c in "!@#$%^&*" for c in password)):
return password
pwd = generate_password(16)
print(pwd)
此函数保证密码强度,且使用加密安全的随机源。
6. 总结判断流程
当你需要生成随机数时,按以下逻辑决策:
- 是否用于安全目的?(如密码、密钥、认证令牌)
- 是 → 必须用
secrets - 否 → 进入下一步
- 是 → 必须用
- 是否需要复现实验结果?(如科研模拟、测试)
- 是 → 用
random并设置seed - 否 → 仍可用
random,但无需设种子
- 是 → 用
永远不要为了“方便”而在安全场景使用 random。多写一行 import secrets,换来的是系统安全的根本保障。

暂无评论,快来抢沙发吧!