Hash

密碼雜湊在中間包含 ‘x00’,導致來自 bcrypt.hashpw 的 ValueError

  • May 25, 2021

我有一些程式碼接受字元串格式的密碼,並在將其傳遞給 bcrypt 進行散列之前使用 SHA3-512 對其進行散列。然而,巧合的是,我發現了一個測試案例,它在雜湊結果的中間產生了一個包含 ‘\x00’ 的雜湊,如下所示:

password_str = 'tes15!tes15!tes15!tes15!tes15!tes15!tes15!tes15!tes15!tes15!tes15!tes15!.'
password_bytes = password_str.encode('utf-8')
hashed = hashlib.sha3_512(password_bytes).digest()

散列中間包含 ‘\x00’ 的散列結果:

b'u~"\x98\xac\xc8E2eV\xbb\x8e#}\x92R\xdc\xa2\xab\xab\xcb\x8d.~\x9f\x82a\xbf\xec]k\xdb\xc55\x1d\xa4\x00\xe8\x03\x94\xb0\x91\x14\xf0\x9ec\x9a\x9ay\xfeP\xe3\x07J\x00\xb5\xbd\xba\xcb(\xf5\xdb\xab\x1a'

正如我們所見,在散列的中間有一個 ‘\x00’,但沒有在末尾。bcrypt 檢測到這是一個 ValueError 異常:

if b"\x00" in password:
   raise ValueError("password may not contain NUL bytes")

我知道 ‘\x00’ 是一個保留字元,並且由於散列長度擴展攻擊可能是攻擊向量。但是,我不認為我做了任何不尋常的事情。是否有一種最佳實踐可以在將雜湊傳遞給我失去的 bcrypt 之前對其進行清理,或者是否有另一種類型的字節編碼應該與密碼一起使用?

僅僅因為它的派生雜湊與使用的身份驗證庫衝突而拒絕普通密碼會很奇怪。非常感謝任何幫助或建議。謝謝!

編輯:發布後,我在 bcrypt 上發現了一些描述我的問題的問題 https://github.com/pyca/bcrypt/issues/55 https://github.com/pyca/bcrypt/pull/57

在傳遞給 bcrypt 之前,您不應該對其進行散列,bcrypt 旨在自行完成散列和密鑰拉伸工作。

它對雜湊結果感到窒息,因為它需要一個冗餘、糊狀、ASCII(或 UTF-8)、不嚴格、使用者輸入的字元串

一般來說,對可能不可信的事物進行散列處理有助於避免各種數字漏洞(例如 Curve25519 中用於清理事物的內部 SHA-512 操作)——但是,在這種情況下,您可以只信任 bcrypt;當輸入一個蹩腳的、使用者創建的密碼(以及一個適當的、隨機生成的鹽😉)時,它的設計目的是安全和良好地工作。


🤔 但是,經過進一步的思考,我認為這裡的行為pyca/bcrypt實際上是不正確的。您應該使用加密雜湊函式將超過 72 個字節(576 位)的密碼轉換為 bcrypt 可以接受的大小。$$ This is only for pigeonholing purposes; it IS still designed to safely stretch cryptographically mediocre passwords. $$

查看他們跟踪器上的票證,以及他們確認已“修復”它的變更集,作者似乎並沒有真正修復他們實現的古怪行為,而只是更新了他們的文件以推薦以下解決方法

一種常見的方法是使用加密雜湊(例如 sha256)對密碼進行雜湊處理,然後對其進行 base64 編碼以防止出現 NULL 字節問題……

這對我來說似乎是草率和奇怪的;例如,它將此類摘要的輸出限制為最多 408 位(使用base85 編碼),從而將 >72 字節的密碼截斷為僅 51 字節。我很想看看更有經驗的密碼學家對此的看法。

這並不否定這個答案的前半部分,但要記住這一點。顯然,即使有變通方法,該方案仍然非常安全(最好的加密方案旨在避免其實現中的一些失誤);將這種任意截斷應用於使用者密碼似乎並不完全正確,超出了 bcrypt 的作者本身應用的範圍。


您可能需要考慮將 72 字節的限制退回給您的使用者,否則您必須將較長的限制至少截斷 30% 以符合pyca的實現怪癖。(我敢肯定,那些關心最大化密碼熵並達到 72 字節限制的人,如果他們研究香腸是如何製作的,最終會更喜歡前者。)

引用自:https://crypto.stackexchange.com/questions/90173