一、六个汉字只值 30 bits
「密码强度评估」这六个字我是随手敲进去的,先入为主觉得它比 8 位随机小写字母难猜得多。界面给的是 30 bits、评分 35、「中」;同一页敲 zqxjvkwm 却是 37.6 bits、43 分。差别不在长度,在字符集:模板 126-131 行的 charset 只有四个加数——小写 26、大写 26、数字 10,121 行那个取反字符类 /[^a-zA-Z0-9]/ 把汉字、emoji、空格统统塞进 32 这一桶。
于是一个汉字按 log2(32)=5 计费,恰好 5 bits。换成 3500 常用字的外推口径,6 个汉字应有 70.6 bits,页面低估了 40.6 bits。
这些数都是在密码强度评估器里逐条敲出来抄界面读数的。另外它不联网也不查库:285 行模板里 fetch、XMLHttpRequest、sendBeacon、localStorage 命中全是 0。
二、长度按 UTF-16 单元计费
第 134 行取的是 pw.length,不是码点数。1 个 U+1F600 占 2 个单元,每单元仍是 5 bits,于是单字符 emoji 显示 10 bits、18 分、「弱」,3 个相同 emoji 共 6 单元、30 bits、35 分。emoji 没被特殊照顾,只是长度翻倍多拿 5 bits,字符集那一栏仍是 32。
更要紧的是扣分。第 148 行的连续重复检测写的是 (.)\1{2,},对增补平面字符失灵:14 个相同汉字与 7 个相同 emoji 熵都是 70 bits,前者被扣 15 分停在 52「中」,后者拿 67 分进「强」,同一份熵两个等级。
三、词典表只有 27 条,命中只动分数
103-108 行那张 commonPasswords 一共 27 条、最长 10 字符。命中一次扣 60 分,156 行的 isCommon 再把等级硬判成「弱」。最扎眼的对照:password 与 xqzmvkdt 熵都是 37.6 bits、在线攻击都是 33 年,区别只有评分 0 对 43——命中词典只改分数与文案,熵和破解时间一个数都没动。
123 行只做 toLowerCase() 全等比对,改一个字符就出表:PASSWORD 照样命中,而 P@ssw0rd 77 分、password123456 63 分都是「强」。
四、评分封顶 70:熵和等级是两条线
等级只看 score(156-164 行):低于 30「弱」、30 到 54「中」、55 到 79「强」、80 以上才「极强」。score 在 140-152 行算:长度项 min(40, len×3)、四类字符各 10 分、熵项 min(20, floor(熵/4)),再减词典 60 分、连续重复 15 分、顺序串 15 分。只用一类字符的口令,天花板就是 40+10+20=70,永远碰不到 80。
| 口令 | 熵 bits | 评分 | 等级 | 在线攻击列 |
|---|---|---|---|---|
password(词典命中) | 37.6 | 0 | 弱 | 33 年 |
Zq8!(4 位四类齐全) | 26.2 | 58 | 强 | 4 天 |
| 1008 位纯小写 | 4738 | 70 | 强 | 宇宙寿命级别 |
Zq8!Km4$vN2&(12 位四类) | 78.7 | 95 | 极强 | 78 万亿年 |
240 位纯数字同样是 70 分;而表里 26.2 bits 的 Zq8! 已经算「强」,在线攻击栏却只剩 4 天。
五、改口比溢出早几百个数量级
第 201 行算的是半空间 2^熵/2,除以 195-200 行写死的四档速度:在线 100 次/秒、离线慢速 1万次/秒、离线快速 100亿次/秒、GPU 1000亿次/秒。列表页 intro 说的「普通 CPU 约 10 亿次/秒」在模板里没有对应档,实测 37.6 bits 的第三行是 10 秒而不是 2 分钟。后三档都是离线速度,量级差见哈希算法的速度差那篇。
真正的浮点溢出要熵到 1024 才让 Math.pow 吐 Infinity:94 字符集要 157 个字符,32(符号与汉字同桶)要 205、26 要 218;emoji 单元只能取偶数,32 桶要 206 单元、即 103 个码点。
可 249-263 行的 formatTime 对 Infinity 与 3.1536e+22 秒回的是同一句「宇宙寿命级别」,比较链对 Infinity 全部为假,直接落到末尾那条 return。94 字符集只要 13 个字符(85.2 bits)在线列就这么写了,此时慢速列还剩 70 万亿年;四行改口的长度分别是 13、14、17、18。
单位链也有舍入怪癖:阈值依次是 0.001 秒「瞬间」、1 秒「< 1 秒」、60、3600、86400、2592000、31536000,再按年分 1000、1e6、1e9、1e12、1e15,显示一律 Math.round。于是 31535999 秒显示「12 个月」,31535999999999 秒显示「1000 千年」而不是「1 百万年」。
六、扣分的两张表对不上
顺序字符扣分表在 150 行有 16 条串,改进建议在 223 行只查 4 条。Zxcde1!q 含 cde 与 def,评分从 77 掉到 62,建议区只有「增加密码长度至12位以上」那句,没提顺序字符;Zsdf9!qq、Z890!qqA 同样被静默扣 15 分。连续重复的门槛是 3 个以上:aaa 熵 14.1 bits 只剩 7 分,aa 反而 18 分。
长度那头反倒全敞开:14 行的 input 没有 maxlength,1000 个四类字符实测熵 6554.6、评分 100,201 次调用中位耗时 44.6 微秒,20000 字符也只有 582.9 微秒。它也没有批量入口,本地不留记录,刷新就回到 0 分与「—」。想要够长又够杂,不如让强密码生成器生成一串再来评估。
Q:汉字口令能不能拿这页下结论?
不能。它给的是分桶上界,6 个汉字算 30 bits,按 3500 常用字外推是 70.6 bits;等级又被种类分与两张惩罚表牵制,同一份 70 bits 能给「中」也能给「强」。真正决定难度的是用字规模与撞库成本,模板都没查。
Q:看到「宇宙寿命级别」能不能收手?
先分清是哪一栏。94 字符集只要 13 个字符、85.2 bits,在线攻击栏就打这句,慢速列此时还剩 70 万亿年;2^熵 真变成 Infinity 要 157 个字符,两句文案逐字相同。
Q:改进建议那一栏要不要照做?
参考但别全信。它只查 4 条顺序串,扣分表却有 16 条;也不会告诉你汉字被算进 32 那一桶、emoji 按 2 个单元计费。