前年我留了一台按固定 IP 对外开放的测试服务器,某天早上收到云厂商短信:账号异地登录成功。翻认证日志,前一晚的 Failed password 有 4.7 万多条,来源 IP 分散在十几个网段——不是有人试密码,是脚本在扫。更难看的是那条成功记录:用户名 root,密码是三年前给一台临时机器随手设的六位数字。那台机器早没人用了,我连密码都想不起来,可 22 端口一直开着。
那次之后我把所有账号的口令重新理了一遍,才明白"密码强度"不是靠"加个符号"解决的。
一、破解不是猜,是按数量级枚举
耗时由两个变量决定:长度和字符集大小。但口径必须先说清楚。下面那两档速率——普通 CPU 约每秒 10 亿次、GPU 集群约每秒 1000 亿次——只适用于攻击方已拿到口令库、库里是未加盐快哈希(MD5、SHA1 一级)的离线撞库。对方若改用 bcrypt、Scrypt、Argon2 这类慢哈希,同样硬件的有效速率要低三到六个数量级,下表每项时长再乘一千到一百万倍。
代进这个最坏口径,结果如下。表里的数你都能自己除:组合数除以 10⁹ 得秒数,再除以一年的秒数约 3.16×10⁷ 得年数。
| 口令构成 | 组合数 | 每秒 10 亿次时的耗时 |
|---|---|---|
| 8 位纯小写字母(26 种) | 约 2.09×10¹¹ | 约 3.5 分钟 |
| 10 位纯小写字母(26 种) | 约 1.41×10¹⁴ | 约 39 小时 |
| 12 位大小写加数字(62 种) | 约 3.23×10²¹ | 约 10.2 万年 |
| 16 位大小写数字加符号(94 种) | 约 3.72×10³¹ | 约 1.18×10¹⁵ 年 |
规律很直白:每多一位,组合数就乘一个字符集大小,26 的四次方是 456976,多一位就是 456976×26。同一件事也可以写成熵,粗算式是熵约等于长度乘以 log2(字符集):16 位、94 种字符,log2(94) 约 6.55,16×6.55 约 105 bit;8 位纯小写只有 8×4.70 约 38 bit。我说"几乎无法破解"依据的是那 105 bit,不是我随手加的符号。所以我给自己定的顺序是:先够长,再谈混合。
二、人写出来的"复杂密码"为什么最先被命中
123456、password、qwerty 这几个串在泄露库里反复出现。NordPass 2024 年那份基于公开泄露数据(含暗网,约 2.5TB 语料)整理的常用口令榜里,123456 排第一、password 第四、qwerty123 第五;更早被反复引用的是 SplashData 从 2011 年起按年发布的那份榜单。两份榜单口径逐年不同,别并成一句"常年霸榜";我只取一个结论:这些串一定在攻击方的第一批字典里。拿到库后第一件事也不是硬算,是跑字典加变形规则:把 a 换成 @、o 换成 0、首字母大写再拼年份,Abc@2024、qazwsx 这类键盘序列是第一批候选,命中比穷举快好几个数量级。我那六位数字危险的地方不在六位,在于它是一段日期——生日、工号、手机号尾数、入职年份这类结构,恰好是人脑最爱写、也最容易被拼出来的。
另一层风险是复用。一个中小站点脱库,账号和口令成对流出,脚本会拿同一组合去试主流邮箱、支付、云厂商、网盘。真正沦陷的往往不是主账号,而是那个被人忘记的老账号——我那次就是这么丢的口子。
三、我实际重做的步骤
- 先列清单:邮箱、云厂商、支付、社交、内网系统、还有权限的老服务,按"泄露后的代价"排序,不按使用频率。顺手发现七八个当年只为领一份资料注册的账号,直接注销或停用。
- 逐行换新口令。我是在随机密码生成器里做的,长度统一取 16,大小写数字符号全开;要在终端手敲的那几条勾上排除易混淆字符,去掉
0/O、1/l/I(勾选项标的是这五个字符)。这一步我不敢将就的是随机源:口令必须由密码学安全随机数产生,浏览器里对应crypto.getRandomValues;Math.random是伪随机,种子一旦被恢复,前后那串都能同样算出来,拿它生成口令等于没随机。"在本地跑、不发后端"只解决了传输环节,随机源对不对是另一件事,两者都得成立。 - 生成完当场存进密码管理器,不贴备忘录、不发聊天窗口。我只保留一个主口令,同样生成得更长,写在纸上放抽屉——前提是它从不出现在登录界面以外的地方。
- 把 22、3389 这类端口从公网收回,改成密钥或走隧道。这一步和密码强度无关,但它才是真正止血的地方。
四、别把强度评估当成保证
评估类工具看字符组成、熵值和理论穷举时间,它衡量的是"随机性",不知道你这条口令是不是谁的生日,也不知道它是不是已经躺在某个泄露库里。所以我一般两步走:先用密码强度评估看评分和熵,确认不是明显的弱结构;再单独问自己一遍"这条口令能不能被认识我的人拼出来"。反过来,随机生成的 16 位口令即使评分是强,只要在同两个站点复用,风险立刻掉到最低档。
- 真正决定安全边界的是对方校验哈希的快慢,用户侧控制不了,只能靠不复用兜底;这也是第一节那些时长必须带口径看的原因。
- 没有配套措施时,定期强改会催生
Abc@2024到Abc@2025这种递增规律,反而更好猜。不鼓励周期性强制改不是我个人的偏好,NIST 的 SP 800-63B 就是这个建议:没有泄露迹象就不做定期强制更换。我现在的做法是只在有泄露迹象时换。 - 两步验证的优先级高于把口令从 16 位加到 24 位。
- 手机号、工号、邮箱前缀这三类"个人信息式口令"要彻底杜绝,同事反向就猜得出来。
五、常见疑问
Q:密码是不是越长越好,有必要超过 20 位吗?
普通账号 16 位混合随机已经够,再加长的收益主要是心理层面。该花力气的是不复用和两步验证。
Q:符号必须加吗?
加符号扩大了字符集,等长下确实更费时间;但如果因此必须靠规律记忆、开始复用,就得不偿失。长度和随机性优先。
Q:在网页上生成的口令会不会被服务器拿走?
这类工具的机制我确认过:口令在浏览器本地产生,不发后端、不写日志,不存在生成过程被听到。更要紧的是随机源得是密码学安全随机数,Math.random 那种可反推的伪随机不算。真正要防的是生成之后的保管——粘进聊天窗口、邮件或截图才算泄露。
Q:十几个账号根本记不住怎么办?
不要试图记。密码管理器负责记每条随机口令,你只记一个主口令;自动填充覆盖日常大多数登录,但支付和云厂商我建议手动输入,避免被填进错误的域名。
Q:怎么知道自己的口令已经泄露了?
两类信号:邮箱里的异地登录和异常验证码请求,以及公开的数据泄露查询。收到告警先改邮箱口令,它是所有其他账号的重置入口。
那台被扫的服务器给我的最大教训不是"密码要复杂",而是:攻击方永远挑最弱的入口,而最弱的入口通常是你已经忘记它还存在的那个。列清单、按丢掉后的代价排序、从第一行开始换,比定一个"以后都用 24 位"的口号现实得多。