長度,而非複雜度劇場,才是任何密碼強度檢查的基礎。一個實用的強度計量器會計算每一個 Unicode 字碼點、回報出現的廣義字元群組數量,並在賦予從弱到強的五級標籤之前,標記出明顯的重複區塊或短遞增序列。NIST SP 800-63B 將短於 15 個字元的密碼視為弱密碼(前提是該密碼為唯一的驗證因素),這就是為什麼透明的本地端審查會優於隱藏其閾值的黑盒評分。正在尋找 Python 密碼強度檢查工具的讀者,往往最後複製了一段強制要求大寫、小寫、數字與符號的正規表示式——而這正是 NIST 明確警告的作法,因為它會產生像 P@ssw0rd1 這類可預測的替換結果。更好的工作流程是:讓一個能說明規則的本地瀏覽器計量器,與一段只在密碼經過預覽並被目標服務接受後才提交的 Python 程式碼,互相搭配。

how to check password strength in python
在 Python 中檢查密碼強度,避開正規表示式陷阱

密碼強度檢查實際上量測的是什麼

真正的強度檢查會看三件事:秘密的原始字碼點長度、這些字碼點的變化程度,以及字串是否含有明顯的人為模式。現行 NIST 指引(記載於 NIST SP 800-63B)將短於 15 個字元的密碼視為弱密碼(前提是該密碼為唯一的驗證因素)。這個單一規則改寫了整個關於複雜度的討論:一個 22 字元的全小寫詞組,會比一個 10 字元、由大小寫混合並強制加上標點符號的混合體更強,因為從外洩資料庫、字典與鍵盤走法中發動攻擊的駭客,會先找到較短的那個。

密碼強度檢查工具正是秉持這樣的理念。它計算的是 Unicode 字碼點,而非 UTF-16 單位,所以一個表情符號算一個字元,而不是兩個。它會回報六個廣義群組(小寫、大寫、數字、符號、空白字元、非 ASCII)中出現了幾個,作為一種描述性訊號,而非強制要求。接著它會檢查兩個範圍明確、可說明的模式:一個 1 到 4 字碼點的短區塊重複至少 3 次,以及一段 4 字碼點的升冪或降冪序列。這兩項檢查中只要觸發任何一項,最終標籤就會被壓低到「普通」,無論字串其餘部分有多長。

為什麼 Python 正規表示式腳本會給出虛假的信心

經典的 Python 教學食譜會把針對大寫、小寫、數字與符號的 re.search 呼叫組合起來,再加上一段長度檢查。這種形狀的腳本會欣然接受一個幾乎出現在所有公開外洩資料庫中的值,因為這種替換方式通過了每一條規則。它也會拒絕一個其實完全沒問題的詞組密碼,例如 "correct horse battery staple",因為該詞組缺少強制要求的符號,而且長度超過了長度檢查原本設定的範圍。

NIST SP 800-63B 明確不鼓勵強制組合規則,正是基於這個原因:使用者會以可預測的替換與重複來回應,而產生出來的密碼比一個較長、看似低熵的詞組更容易被攻擊。OWASP 驗證備忘單更進一步,建議以一份持續更新的常見、預期與已遭破解值的封鎖清單進行篩選——這是任何正規表示式鏈都無法涵蓋的步驟。

一個只用教學食譜拼湊出來的 Python 驗證器,會放過攻擊者很快就能猜到的密碼,卻又拒絕那些能抵擋字典攻擊多年的秘密。解方不是更聰明的正規表示式;而是一種不同的評估模型——先測量長度、接受多樣性而非強加多樣性,並在儲存前以最新的外洩清單檢查整個值。

用本地瀏覽器計量器檢查密碼

在你的 Python 程式碼真正接受密碼之前,最乾淨的預覽方式,就是先讓它通過一個透明的本地計量器。密碼強度檢查工具會在瀏覽器內執行上述規則。不會上傳、儲存或送往任何外洩服務,除非你按下「顯示」,否則輸入內容會保持隱藏。

  1. 在瀏覽器中開啟密碼強度檢查工具。輸入欄位預設會被遮罩,所以任何瞥向螢幕的人只會看到圓點,而不是真正的密碼。
  2. 輸入或貼上候選密碼。觀察長度計數器以字碼點為單位即時更新,包含你輸入的任何表情符號或非 ASCII 字元。
  3. 檢視描述性的字元群組計數。它會標示小寫、大寫、數字、符號、空白字元與非 ASCII 字元的出現情形,但並不要求每一類都必須出現——它只是回報組合,而非強制組合。
  4. 閱讀模式旗標。若出現「偵測到模式」字樣,代表計量器發現一段短重複區塊,或一段 4 字元的升冪或降冪序列,最終標籤會被壓低到「普通」。
  5. 逐條閱讀建議。工具會建議更長的秘密、改用詞組密碼而非模式,或對照帳號服務商的外洩清單進行檢查。
  6. 把最終得到的標籤當作指引,而非認證。「強」的分數只涵蓋本文記載的長度與兩項狹義的模式檢查。

把本地檢查整合進 Python 工作流程

Python 開發者通常會在表單層與驗證器兩處與密碼相遇。瀏覽器計量器能在這兩個時刻之間攔截掉大多數的糟糕選擇,而無需撰寫正規表示式。

  1. 在表單層向使用者索取密碼。在它被雜湊之前,不要回顯、記錄或寫入磁碟。
  2. 在另一個分頁中開啟密碼強度檢查工具,並貼上候選密碼。由於計量器在本地端執行,秘密的任何部分都不會離開你的機器。
  3. 對於任何以密碼作為唯一因素的帳號,確認標籤至少為「良好」(15 到 19 個無模式的字碼點)或「強」(20 個以上字碼點)。
  4. 透過閱讀目標服務的密碼政策,確認它接受你所輸入的每一個字碼點,包括任何非 ASCII 字元或空白。有些系統會以改變儲存值的方式對輸入進行正規化。
  5. 將完整的值與服務的外洩或封鎖清單進行比對——可以使用服務商自家的篩選端點,或一份下載下來的常見密碼清單。本地計量器刻意不執行這個步驟。
  6. 只有在這五項檢查全部通過後,才透過 TLS 提交密碼。並使用具備加鹽的密碼雜湊函式儲存,例如 Argon2id、bcrypt 或 scrypt——絕不要使用純 SHA-1、SHA-256 或可逆加密。

這種搭配讓 Python 程式碼專注於傳輸與儲存,而讓一個透明的計量器去處理正規表示式假裝在做、卻做不好的啟發式檢查。

五級評分對照表

本地計量器發布的精確分級如下重現。空輸入、短輸入、序列輸入、重複輸入、一個 14 字元的值、一個 11 字元的混合值、一個 19 字元的詞組,以及一個 28 字元的詞組密碼,這八個樣本決定了分界。

長度(Unicode 字碼點)模式結果標籤
0—無
1–7任意弱
8–14任意普通
15 以上偵測到模式上限為普通
15–19無模式良好
20 以上無模式強

此標籤僅涵蓋兩項已記載的檢查:長度區間與狹義的模式測試。它不會估算熵值、不會查詢外洩資料庫,也不會量測對運行現代機率模型的 GPU 叢集的抵抗能力。

超越評分之外

「強」的標籤是起點,而非保證。攻擊者不會列舉每一種組合;他們會從外洩資料庫、字典、服務專屬詞彙、鍵盤走法、個人事實與機率模型中下手。NIST 本身就指出,為使用者挑選的密碼估算熵是困難的,這也是本地計量器拒絕發布熵位元數或破解時間數字的原因。

三個習慣能完成剩下的工作:

  • 把密碼以獨立項目儲存在密碼管理工具中,例如 1Password、Bitwarden 或瀏覽器內建的管理工具。為每個帳號產生不同的隨機秘密,讓單一外洩不會在多個服務之間連鎖蔓延。
  • 為電子郵件、金融、雲端與管理者登入啟用多因素驗證。具防釣魚能力的 passkey 或硬體金鑰,會優於簡訊與 TOTP 碼,因為它們能拒絕被假登入頁面轉傳。
  • 即使本地計量器顯示為「強」,仍應遵循帳號服務商的外洩警示。若服務通知你該值已出現在外洩中,立即在所有重複使用該密碼的網站上更換它,而不是等待某個固定的行事曆日期。

服務營運商承擔另一半的責任。以 Argon2id、bcrypt 或 scrypt 加鹽,選擇高工作因子,並且絕不儲存明文密碼或任何可逆加密的密碼。計量器是使用者端的工具;儲存端則屬於開發者。

若想做並排比較,可在不同分頁中分別把一段長詞組密碼與一段較短的混合字串送進密碼強度檢查工具。上表的精確分級與工具實際套用的完全相同;唯一的差別只是你送進去的輸入。

若想更深入了解,請參閱 在瀏覽器中建立強健的 PDF 密碼——無需上傳。

若想更深入了解,請參閱 如何正確判斷密碼強度。