不能用random模块生成密钥或token,因其基于可预测的伪随机数生成器,不满足密码学安全要求;应使用secrets模块调用操作系统加密随机源。

为什么不能用random模块生成密钥或token
因为random模块基于伪随机数生成器(如Mersenne Twister),其输出可被预测,不满足密码学安全要求。用它生成API密钥、重置令牌或盐值,等于把锁换成纸糊的——攻击者只要拿到几个输出,就能反推内部状态。
真正该用的是secrets模块,它直接调用操作系统提供的加密安全随机源(Linux上的/dev/urandom,Windows上的BCryptGenRandom),无法被预测或重现。
secrets.token_bytes()和secrets.token_hex()的区别与选型
这两个函数都返回密码学安全的随机字节,但输出格式不同,适用场景也不同:
-
secrets.token_bytes(n)返回n个原始字节,适合直接用于加密操作(如AES密钥、HMAC盐) -
secrets.token_hex(n)返回2*n长度的十六进制字符串(比如secrets.token_hex(16)→ 32位hex字符串),适合日志、URL、数据库存储等需要可读/可传输的场景 - 别误以为
token_hex(16)生成的是16字节——它其实是32字符的hex串,对应16字节;若要32字节密钥,应传32而非16
生成安全密码时,secrets.choice()比random.choice()关键在哪
当你要从字符集里逐个挑字符拼密码(比如大小写字母+数字+符号),必须用secrets.choice(),而不是random.choice()。前者对每个字符都做一次独立的系统级熵采样;后者复用同一个PRNG状态,导致整个密码的熵远低于预期。
立即学习“Python免费学习笔记(深入)”;
示例:生成12位含大小写+数字的密码
import secrets import string <p>alphabet = string.ascii<em>letters + string.digits password = ''.join(secrets.choice(alphabet) for </em> in range(12))</p><h1>✅ 每次调用 secrets.choice() 都是全新熵源</h1><h1>❌ 不要用 random.choice(alphabet) 替代
注意:secrets.choice()不支持weights参数,也不支持非序列对象——它只接受序列(list/tuple/str),且必须是内存中已存在的对象。
生成URL安全token时,secrets.token_urlsafe()的长度陷阱
secrets.token_urlsafe(n)返回一个经过base64url编码的字符串,长度约ceil(4*n/3),但实际可用字节数仍是n。比如token_urlsafe(32)大概返回44字符,但背后仍是32字节的真随机数据。
常见错误是按最终字符串长度反推熵值。例如认为“44字符的URL token一定比32字符的hex token更安全”——错。安全性取决于原始字节数,不是编码后长度。
建议:明确按字节需求指定n,而不是按显示长度凑数。Web API的bearer token通常至少需32字节,就直接写secrets.token_urlsafe(32)。
另外,token_urlsafe()结果不含+、/、=,可直接放进URL或HTTP头,无需额外urlencode——这点比手写base64.urlsafe_b64encode(secrets.token_bytes(32)).decode()更稳妥,因为后者若忘记.decode()会返回bytes,容易引发类型错误。


















