保護內容並使用“相同”密碼登錄
我正在編寫一個客戶端應用程序,它希望通過儲存服務儲存一些秘密資訊。客戶端必須使用服務對使用者進行身份驗證,並且服務不應該能夠解碼它為使用者儲存的資訊。針對服務的身份驗證必須基於使用者名 - 密碼組合(我也不能使用本地儲存在客戶端上儲存加密密鑰)。
我想盡量減少使用者必須記住的資訊(願意為此犧牲一些安全性但不會太多)。理想情況下只有使用者名加 1 個密碼。是否有一個很好理解的加密方案允許我這樣做?
如果不是,以下工作效果如何(從安全形度來看):
- 客戶端應用程序向使用者詢問使用者名 (U) 和密碼 (P)。
- 使用加密雜湊 H(),客戶端使用使用者名 U 和密碼 H(P) 註冊使用者。
- 然後客戶端使用 P 使用一些基於密碼的加密(例如 PBKDF2)對要儲存的秘密進行加密。
- 然後客戶端使用 U 和 H(P) 向服務進行身份驗證,並上傳使用 P 加密的秘密資訊。
想法是伺服器只會看到 H(P),因此無法解密秘密(這需要 P 的知識),並且 H(P) 是服務的合理密碼。
如果您想保持加密簡單並且主要在伺服器端(這可能是一個不錯的選擇,例如,如果您正在開發 Web 服務),您可以使用以下方案:
- 要登錄,客戶端發送密碼 $ P $ 以明確的方式發送到伺服器,最好通過安全通道(例如 HTTPS)。
- 伺服器派生一個“主密鑰” $ K_0 $ 從 $ P $ 使用密鑰拉伸KDF(例如PBKDF2或scrypt)和鹽 $ S $ 儲存在數據庫中。的長度 $ K_0 $ 應該理想地匹配 KDF 的自然輸出長度(例如 256 位用於 scrypt 或 PBKDF2-HMAC-SHA256),並且應該至少為 128 位。
- 伺服器使用快速 KDF(例如HKDF;請注意,可以安全地跳過 HKDF 的提取部分,因為 $ K_0 $ 已經是適合直接擴展的 PRK)來導出驗證密鑰(即“密碼雜湊”) $ V $ 例如,128 位,與數據庫中的相應值進行比較,以判斷密碼是否正確。如果是,伺服器可以使用相同的 KDF 來派生額外的密鑰材料,例如任意數量的文件加密密鑰 $ K_i $ , 從 $ K_0 $ . 在派生所有必要的密鑰之後, $ K_0 $ 可以而且應該被丟棄。
在需要和實用的情況下,推導的計算密集部分 $ K_0 $ 從 $ P $ 在客戶端也同樣可以很好地執行。然而, $ K_0 $ 仍然應該通過安全通道傳輸到伺服器,因為在這樣的協議下,它仍然是有效的“密碼等效身份驗證器”。
在這種方案下,伺服器只儲存 $ S $ 和 $ V $ (當然還有加密文件和任何相關材料,例如 IV),這些不足以恢復 $ K_0 $ 或任何文件加密密鑰。蠻力攻擊 $ P $ 由於用於推導的緩慢 KDF 應該是不切實際的 $ K_0 $ 從中; 類似的攻擊 $ K_0 $ 由於密鑰空間的大小,它本身是不可行的。
當然,這個方案的主要和明顯的弱點是惡意或受感染的伺服器仍然可以獲取 $ K_0 $ ,因此還有任何文件加密密鑰,當使用者登錄時。因此,儘管使用者只要不主動使用該服務就可以免受伺服器危害,但他們確實需要相信伺服器目前沒有受到危害他們登錄的那一刻。
只要在伺服器上進行實際的文件加密,上述的弱點就無法避免。如果這不是必需的,顯而易見的替代方法是完全在客戶端執行文件加密(可以使用與上述基本相同的密鑰派生過程,只是全部在客戶端完成)並完全控制客戶端(即在客戶端上沒有執行伺服器提供的加密程式碼),並且簡單地將伺服器視為啞文件儲存設備。然後可以使用增強的PAKE協議(例如SRP )安全地向伺服器驗證使用者身份,可能(但不一定)使用與派生加密密鑰相同的密碼。
編輯: ps。即使伺服器不能支持 SRP 或其他一些增強的 PAKE 協議(例如,如果伺服器執行一些您無法修改的預先存在的軟體),自定義客戶端仍然可以通過派生在其上安全地儲存加密文件文件加密密鑰和伺服器“密碼”(例如,128 位密鑰,ASCII85編碼以實現互操作性)都來自使用者的實際密碼,使用如上所述的 KDF。不管伺服器有多不安全,洩露派生密碼最壞的情況是允許攻擊者以使用者身份登錄伺服器,但不會洩露使用者的實際密碼或文件加密密鑰。
這裡的一個困難是,如果沒有伺服器的幫助,可能沒有任何方便的地方來儲存 PBKDF2 / scrypt 的鹽。解決該問題的一種方法可能是從使用者名和伺服器名稱中獲取鹽;這並不理想,但對於大多數用途來說已經足夠了,特別是如果您在從主密鑰派生每個文件加密密鑰時還使用隨機的每個文件鹽(作為加密文件的一部分儲存)。
事實上,即使您確實將鹽儲存在伺服器上,將使用者名和伺服器名稱附加到它可能仍然是一個好主意,因為它可以防止一些理論上的攻擊(受感染的伺服器躺在鹽附近尋找如果兩個使用者具有相同的密碼,則退出)以及更實際地防止執行不善的鹽生成。