我给自己写了一个本地密码生成器:随机、可读、用完不留痕
目录
- 缘起:密码管理最烦的不是记不住
- 需求拆解:生成出来还要适合使用
- 技术选型:安全随机源比漂亮界面重要
- 核心实现一:生成真正不可预测的字符
- 核心实现二:可读口令与强度评估
- UI 定制:敏感内容不应该停留太久
- 三个密码工具容易给人的错误安全感
- 结尾:生成器只负责随机,保管仍然需要边界
1. 缘起:密码管理最烦的不是记不住
我以前设置密码时经常陷入一个循环:先想一个容易记的词,再补数字和符号;网站提示不符合规则,就继续加字符;下次忘记时,又只能点“忘记密码”。
常见的在线密码生成器看起来很方便,但我始终有几个顾虑:
- 生成的密码可能经由网页脚本或接口传到服务器;
- 页面会把密码历史、收藏和同步作为主要卖点;
- 复杂字符虽然强,却不一定能顺利输入到所有设备;
- 生成后复制到剪贴板,过很久仍然留在那里。
我需要的不是一个“密码保险箱”,而是一个临时工具:本地生成、参数透明、复制后自动清理,不替我保存主密码,也不把安全责任包装成一个分数。
2. 需求拆解:生成出来还要适合使用
我把工具拆成两种生成方式:完全随机密码适合重要账户,可读口令适合需要手动输入的临时场景。
| 诉求(人话) | 拆解后(可验收) |
|---|---|
| 随机性可靠 | 使用操作系统安全随机源,不使用时间或普通伪随机数 |
| 规则清楚 | 可设置长度、字符集和是否排除易混字符 |
| 手动输入方便 | 支持由本地词表生成的多词口令 |
| 知道强不强 | 显示长度、字符空间和估算熵,不假装绝对安全 |
| 复制后少留痕 | 提供剪贴板自动清除倒计时 |
| 不被意外保存 | 默认不保存生成历史、配置中的密码和日志 |
工具不会检查“这个密码是否曾经泄露”,因为那需要把密码发送给外部服务或维护一份不可能完整的泄露库。它只负责生成和解释,不冒充在线安全检测平台。
心得:安全工具最重要的功能之一,是明确告诉用户它没有做什么。
3. 技术选型:安全随机源比漂亮界面重要
程序使用 Python 和 tkinter,核心随机能力来自标准库 secrets:
| 能力 | 方案 |
|---|---|
| 安全随机字符 | secrets.choice() |
| 随机字节 | secrets.token_bytes() |
| 可读口令 | 本地词表 + secrets.choice() |
| 字符串校验 | string 与自定义规则 |
| 剪贴板操作 | tkinter |
| 配置保存 | 只保存非敏感偏好 |
我没有使用 random 模块生成密码。普通伪随机数适合模拟和抽样,不应该承担凭据生成任务。随机种子、当前时间和进程信息都不应成为密码的主要不可预测性来源。
词表也随程序一起保存在本地,不从网络动态下载。词表内容可以公开,真正的随机性来自安全随机选择以及足够的组合空间。
4. 核心实现一:生成真正不可预测的字符
字符集由用户选择,但每个字符都通过 secrets.choice 抽取。为了避免生成结果恰好缺少某一类字符,程序先从选中的每个类别中抽取至少一个,再打乱全部字符。
import secrets
import string
def build_password(length, use_upper, use_digits, use_symbols):
groups = [string.ascii_lowercase]
if use_upper:
groups.append(string.ascii_uppercase)
if use_digits:
groups.append(string.digits)
if use_symbols:
groups.append("!@#$%^&*_-+=")
if length < len(groups):
raise ValueError("长度不足以覆盖已选择的字符类别")
required = [secrets.choice(group) for group in groups]
alphabet = "".join(groups)
remaining = [secrets.choice(alphabet) for _ in range(length - len(required))]
result = required + remaining
secrets.SystemRandom().shuffle(result)
return "".join(result)
“每类至少一个”是方便用户通过网站规则校验的工程约束,它不会替代长度和字符空间带来的随机性。字符集越小,长度就越重要;为了避开少量符号而把密码缩短,往往得不偿失。
生成结果不会出现在调试日志里。界面刷新时也不把旧密码放进撤销栈,避免用户按快捷键后把敏感内容重新显示出来。
5. 核心实现二:可读口令与强度评估
有些账户必须在手机、电视或远程终端上手动输入,纯随机符号密码并不方便。这时可以使用多个随机词组成的口令:
def make_passphrase(words, count=5, separator="-"):
if count < 3:
raise ValueError("口令至少使用三个词")
selected = [secrets.choice(words) for _ in range(count)]
return separator.join(selected)
词表中的单词不需要秘密,但必须足够大、没有明显重复和规律。口令估算熵可以用近似公式:
import math
def estimate_entropy(alphabet_size, length):
if alphabet_size <= 1 or length <= 0:
return 0.0
return length * math.log2(alphabet_size)
这个数字假设每个位置独立且均匀随机。实际密码如果被用户修改、重复使用或受到网站规则限制,安全性会下降。界面因此把它标为“理论估算”,不显示“绝对安全”之类的结论。
强度提示只根据当前生成规则显示:长度、字符空间、是否使用重复字符和估算熵。它不会把一个用户手动输入的密码上传分析,也不会因为分数高就推荐重复使用。
6. UI 定制:敏感内容不应该停留太久
界面分成两个模式:随机密码和可读口令。参数改变后只更新规则说明,用户点击“生成”时才产生新结果,避免每次拖动滑块都在屏幕上刷出大量密码。
生成结果默认使用等宽字体,并提供“显示/隐藏”切换。复制按钮旁边显示倒计时,例如“剪贴板将在 30 秒后清除”。清除操作会先确认剪贴板内容仍然是程序刚刚写入的那一段,避免把用户后来复制的新内容误删。
def clear_clipboard_if_unchanged(root, value):
try:
if root.clipboard_get() == value:
root.clipboard_clear()
root.update()
except Exception:
pass
退出程序时,界面会清空输入框和生成结果。配置文件只保存窗口大小、上次选择的字符类别等非敏感偏好;不会保存密码、口令、生成历史和剪贴板内容。
我还加了一个“暂停屏幕显示”的快捷键。临时离开座位前,可以把结果区域替换为遮罩;它不是安全边界,却能减少密码长时间暴露在屏幕上的机会。
7. 三个密码工具容易给人的错误安全感
坑一:长度和复杂度不能互相替代。 一个包含很多符号但只有八位的密码,未必比一组足够长的随机词组更好。工具同时展示长度和字符空间,不用单一颜色给出武断结论。
坑二:密码生成器不等于密码管理器。 生成后复制到聊天窗口、截图或明文笔记里,工具本身再安全也没有意义。程序会提示用户把重要凭据交给专门的密码管理器保存,但不会内置一个未经充分验证的存储箱。
坑三:排除易混字符会减少空间。 去掉 O/0、l/1 等字符可以改善手动输入,却会降低字符集大小。工具显示实际使用的字符空间,并建议用增加长度来补偿,而不是把“易读”标成“更安全”。
另外,网站的密码规则可能禁止某些符号或限制最大长度。生成器只能提供候选结果,最终仍要以目标服务的规则和账户安全策略为准。
8. 结尾:生成器只负责随机,保管仍然需要边界
做完这个工具后,我不再为每个新账户临时拼密码,也不会为了方便把同一个密码改几个数字继续使用。生成、复制、清除的过程变得很短,但责任边界反而更清楚:
- 随机性由系统安全随机源提供;
- 强度只做理论估算,不夸大结论;
- 重要凭据交给专门工具保管;
- 剪贴板和屏幕上的明文尽快清除;
- 不把生成历史和密码写进日志。
如果你也想写一个密码生成器,可以先从 secrets.choice、长度设置和剪贴板清除开始。不要急着加入云同步、账号体系和“自动判断是否安全”,因为这些功能会把一个本地小工具变成需要长期维护的安全产品。
生成密码并不难,难的是始终记得:随机只是第一步,保管、使用和不重复才决定最终风险。
- 点赞
- 收藏
- 关注作者
评论(0)