一、六個漢字只值 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 個單元計費。