基於秘密和公共標識符安全地生成密碼
我正在嘗試創建一個好的方案來在我的系統管理腳本中生成密碼。我想做的是有一個密鑰(比如說
hello123
),我只保留在受信任的管理機器上。然後,我想將它與“密碼標識符”(例如testing-db-pass
)結合起來,並生成一些 ASCII 文本,這些文本可以在服務配置中設置為密碼。我已經閱讀了關於密鑰拉伸、PBKDF2 和 HMAC 的資訊,並且似乎使用 bcrypt 和 pass
hello123
,testing-db-pass
因為它們的 salt 和 key 會起作用——但這容易受到攻擊嗎?我需要做類似 HMAC-bcrypt 的事情嗎?有沒有更好的辦法?
我在我的一個密碼生成器中所做的是給定一個密鑰 $ K $ , 公開資料 $ \text{Pub} $ ,我首先生成一個可靠的“萬能鑰匙” $ K_m $ 通過使用 PBKDF2 對密鑰進行密鑰拉伸(任何其他密鑰派生函式都可以,我只是碰巧有那個):
$$ K_m = PBKDF2(K, \text{salt, iterations, } \cdots) $$ 然後通過 $ HMAC(K_m, \text{Pub}) $ (根據需要格式化結果)。
它非常簡單且安全,但是您需要注意一些難點:
- 如果您的公開數據 $ \text{Pub} $ 從多個來源聚合(例如,標識符字元串 + 時間 + 計數器 + 版本 + 其他元數據),您必須確保最終不會生成重複 $ \text{Pub} $ 來自不同原始輸入的數據(當然,或者您會得到相同的派生密碼)。例如,不要連接字元串來創建 $ \text{Pub} $ . 相反,將它們全部散列,並將散列連接起來。正式地,“輸入數據 $ \to \text{Pub} $ " 映射必須是無碰撞的。在實踐中這很容易出錯。
- 確保正確設置了所有安全參數(PBKDF2 迭代次數,正在使用強雜湊函式,所有這些)
像這樣使用 HMAC 而不是僅僅使用 PBKDF2 和弄亂鹽的好處是:
- 它可擴展-可以通過單個 PBKDF2 呼叫有效地計算多個派生機密,而您的方法將涉及為每個派生密碼呼叫密鑰派生函式(如果您使用大量迭代,這可能非常慢)
- 它實際上是在正確使用底層加密原語。密鑰派生函式並不意味著通過更改鹽從給定的密鑰中派生出許多單獨的密碼。有關詳細資訊,請參閱此問題。這不是 HMAC 的情況,事實上,它應該像這樣使用,因為它的主要用途是使用加密密鑰生成任意消息的指紋(當然你不需要在每條消息)。當然,您可能會很好,但是 PBKDF2 並不是要以這種方式使用。
- 它不太容易被濫用。您的方法沒有中間步驟,只有輸入和輸出。這使得很難推斷算法中每個變數具有的安全屬性,以及算法創建的任何“密碼障礙”(例如,“無法從給定的生成密碼中導出密鑰”)。這很重要,因為當您需要在派生過程中添加更多功能時,您將需要再次重做整個密碼分析,以確保您沒有從根本上破壞任何東西。在我建議的方法中,中間“主密鑰”具有一些已知的安全屬性(例如,密鑰不能被任何對主密鑰進行操作),這意味著我可以輕鬆地添加其他功能,而不必擔心引入這樣那樣的安全漏洞。如果您曾經聽說過“結構化程式”這個術語,那麼這幾乎就是它的一個例子。
說到添加更多功能,您可能需要一種機制來驗證您輸入的密鑰是否正確(否則您將得到一個與您之前使用的密碼不匹配的垃圾密碼)。使用您的方法如何做到這一點並不明顯。然而,使用改進的算法,這相當簡單——生成並儲存隨機鹽 $ S $ 您提供給 PBKDF2 以生成 $ K_m $ ,然後儲存 $ Hash(K_m) $ 某處,使您能夠驗證是否負責正確鍵入的密鑰,而不會損害它(或任何派生的密碼)如果此儲存的資訊以某種方式洩漏(這類似於將密鑰的雜湊儲存在文件中時加密文件,以便能夠告訴使用者他是否正確鍵入了密鑰,而不是遵守和解密垃圾)。
最後,一旦你有了最後的字節,就很容易將它們轉換為可以用作密碼的字元集。最簡單的方法就是轉成Base64,相當方便高效。當然,由於它們只是位,您可以將它們轉換為您想要的任何內容(十六進制、原始 ascii、pin 號等)