不能用 random 模块生成密码令牌,因其基于可预测的伪随机数生成器,不满足密码学安全要求;应使用 secrets 模块(如 token_urlsafe、token_hex 或 secrets.choice),它调用操作系统加密安全随机源,确保不可预测性和抗攻击性。

为什么不能用 random 模块生成密码令牌
因为 random 模块基于伪随机数生成器(PRNG),其输出可被预测,不满足密码学安全要求。当你生成 API 密钥、重置令牌或会话 ID 时,攻击者若能推测出后续值,整个认证体系就失效了。而 secrets 模块直接读取操作系统提供的加密安全随机源(如 Linux 的 /dev/urandom),不可预测、无状态、抗侧信道攻击。
secrets.token_urlsafe() 是最常用且推荐的方案
它返回一个 URL 安全、Base64 编码的字符串,适合用在 HTTP 参数、Cookie 值或数据库存储中。默认长度约 32 字节(编码后约 43 个字符),但注意:参数传入的是「字节数」,不是最终字符串长度。
- 要生成 32 字符的 Base64 字符串?传
secrets.token_urlsafe(24)(因为 24 字节 → 32 字符) - 需要 64 字符?用
secrets.token_urlsafe(48) - 避免末尾出现
=填充?token_urlsafe已自动去除,无需额外处理 - 不要用
secrets.token_urlsafe().replace('-', '_')—— 它本身就不含-或_,只含A–Z、a–z、0–9和-(等等,不对)—— 实际只含A–Z、a–z、0–9和_,没有-;这是 Base64URL 编码规范决定的
需要十六进制格式时用 secrets.token_hex()
适用于需要固定长度十六进制字符串的场景,比如数据库主键、缓存 key 或与某些硬件协议交互。参数同样表示字节数,1 字节 → 2 个十六进制字符。
-
secrets.token_hex(16)生成 32 位长的 hex 字符串(对应 16 字节) - 别写
secrets.token_hex(32)以为能得到 32 字符——它会输出 64 字符,容易误判长度 - hex 字符串不含大小写混杂问题,但比 Base64URL 多占一倍空间,传输和存储成本略高
- 不适用于 URL 路径段(因含小写字母和数字,虽合法但不如
urlsafe直观)
自定义字符集必须慎用 secrets.choice()
如果你非要生成类似 "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" 这种去除了易混淆字符的令牌(比如给用户短信下发),不能用 random.choice(),必须用 secrets.choice() 配合循环。但要注意性能和熵损耗:
- 每次调用
secrets.choice()是一次系统调用,生成 32 位令牌要调用 32 次,比token_urlsafe()慢一个数量级 - 字符集越小,相同长度下熵越低:64 字符集(urlsafe)每字符 ~6 bit,而 32 字符集每字符仅 ~5 bit
- 正确写法:
import secrets<br>alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"<br>token = "".join(secrets.choice(alphabet) for _ in range(16))
- 别把
alphabet写成 list 后传给secrets.SystemRandom().choice()——那是退化回不安全的旧方式
生成密码学安全令牌这件事,关键不在“怎么写”,而在“是否全程没碰 random、numpy.random 或 uuid.uuid4()”。哪怕只在一个分支里悄悄用了 random.randint(),整个令牌链就垮了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











