不能用 random 模块生成密码,因其为伪随机、种子可预测;应使用 secrets 模块,它调用操作系统熵源(如 /dev/urandom),满足 csprng 要求;secrets.choice() 适用于自定义字符集和精确长度,secrets.token_urlsafe() 返回 base64 编码字符串、长度不精确。

为什么不能用 random 模块生成密码
因为 random 是伪随机,基于确定性算法,种子可预测;密码场景必须用密码学安全的随机源。secrets 模块底层调用操作系统提供的熵源(如 Linux 的 /dev/urandom),满足 CSPRNG(加密安全伪随机数生成器)要求。
secrets.choice() 和 secrets.token_urlsafe() 怎么选
两者适用场景不同,选错会导致密码强度不足或格式不兼容:
-
secrets.choice()适合自定义字符集(比如排除易混淆字符0O1lI),但需手动控制长度和字符分布 -
secrets.token_urlsafe()直接返回 base64 编码字符串,含大小写字母、数字和-_,长度是近似值(实际字节数决定,解码后长度不固定) - 若需精确长度(如 12 位纯字母数字),必须用
secrets.choice()循环拼接,不能依赖token_urlsafe(n)的n参数——它表示最小字节数,不是最终字符串长度
生成高安全密码的实操写法(含常见避坑点)
以下是一个兼顾可读性、强度和兼容性的生成函数:
import secrets
import string
<p>def gen_password(length=12):</p><h1>排除易混淆字符:0/O/1/l/I</h1><pre class="brush:python;toolbar:false;">chars = string.ascii_letters + string.digits
chars = chars.replace('0', '').replace('O', '').replace('1', '').replace('l', '').replace('I', '')
return ''.join(secrets.choice(chars) for _ in range(length))示例:生成 16 位密码
print(gen_password(16))
注意几个关键点:
- 不要用
random.choice()替换secrets.choice(),哪怕只改一行也会破坏安全性 - 避免在循环中重复调用
secrets.SystemRandom().choice()——secrets.choice()内部已优化,更简洁且等效 - 如果系统需要特殊符号(如
!@#$%),需显式加入字符集,并确认目标系统支持这些符号(比如某些旧数据库字段不支持$)
验证密码是否真随机:别信肉眼判断
肉眼看到“看起来乱”不等于安全。真正要检查的是生成逻辑是否依赖 secrets,以及字符集是否足够大(建议 ≥ 64 种可选字符)。可以用以下方式粗略验证:
from collections import Counter pwd = gen_password(1000) char_dist = Counter(pwd) print(len(char_dist)) # 应接近你定义的字符集长度(如去掉5个后剩 57)
如果输出远小于预期(比如只有 20 多种字符),说明字符集构造出错或被意外截断——常见原因是字符串替换链写错顺序,或用了不可变操作却没赋值回变量。
真正难的不是写几行代码,而是确保每一步都落在密码学安全路径上:从熵源选择,到字符过滤逻辑,再到最终拼接,任意一环掉链子,密码就形同明文。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











