不能用 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><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/00968c3c2c15" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">Python免费学习笔记(深入)</a>”;</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill8054" title="python-parallelization"><img
src="https://img.php.cn/upload/skill/000/000/081/179181013081932.jpg" alt="python-parallelization" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill8054" title="python-parallelization">python-parallelization</a>
<p>将顺序 Python 代码转换为并行/并发实现,用于在需要并行化 Python 代码、提升性能时使用。</p>
</div>
<a href="/xiazai/skill8054" title="python-parallelization" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><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 多种字符),说明字符集构造出错或被意外截断——常见原因是字符串替换链写错顺序,或用了不可变操作却没赋值回变量。
真正难的不是写几行代码,而是确保每一步都落在密码学安全路径上:从熵源选择,到字符过滤逻辑,再到最终拼接,任意一环掉链子,密码就形同明文。

















