Try   HackMD

如何安全的加密使用者的密碼 使用 PBKDF2 以及破解的可能性

問題

使用者的密碼不得使用明文儲存於資料庫,避免資料庫管理員看的到密碼
同時密文也不可以使用可逆向解密的演算法,避免資料庫外洩後被解出來

為了達成上述兩個目的,會使用 hash 類的演算法,破壞可逆性後儲存

傳統的hash演算法 (如 md5) 已於2016被認定為不安全的 hash 演算法了,坊間有多個 md5 線上比對的網站,可以用下列的網站先產生md5,再拿去對照的網站測試結果,就會發現弱密碼幾乎都可以被反向解回來

md5 以外的 hash 演算法

md5 之後較新的演算法是 sha 演算法,可以搭配加鹽的方式避免碰撞攻擊,但 sha1 也已經在2017年被驗證可以被碰撞攻擊了,所以至少要使用 sha256 以上
但sha256也有其他問題,因為電腦運算速度一直提升,難保有一天電腦效能增加,sha256也被攻破

PBKDF2 演算法介紹

PBKDF2 演算法是一種實現 密鑰延伸(key stretching) 類型的演算法,這類型的演算法是為了對抗電腦運算速度太快,避免密碼被暴力破解的解決方案。

PBKDF2會先使用 hash 類型的演算法,將 hash 的結果當成hash 演算的鹽,重新再跑一次 hash,反覆遞迴運算。

因此只要將遞迴 hash 的次數調升,就可以輕鬆地讓運算速度降低,減緩運算速度快的破解時間。

PBKDF2 + SHA 雜湊的不可逆演算法,適用於把使用者密碼加密後存入資料庫

http://vegeee-csharp.blogspot.com/2019/05/password-hash-using-pbkdf2-with-hmac.html?m=1

sha = 不可逆雜湊,加密速度快,但運算位數少時容易被碰撞攻擊
pdbdf2 = 可以把演算法加鹽並多跑n次遞回的演算法,速度慢

pbkdf2 + sha = 把sha演算法跑很多次,變成慢速不可逆演算法

完整程式碼

也可以點此觀看執行結果範例
https://dotnetfiddle.net/JQX1AK

using System; using System.Security.Cryptography; namespace pbkdf2_sha2 { class PasswordHash { private const int SaltByteSize = 32; private const int HashByteSize = 32; private const int Iterations = 4096; private static string GetSalt() { var cryptoProvider = new RNGCryptoServiceProvider(); byte[] b_salt = new byte[SaltByteSize]; cryptoProvider.GetBytes(b_salt); return Convert.ToBase64String(b_salt); } public static string GetPasswordHash(string password) { string salt = GetSalt(); byte[] saltBytes = Convert.FromBase64String(salt); byte[] derived; using (var pbkdf2 = new Rfc2898DeriveBytes( password, saltBytes, Iterations, HashAlgorithmName.SHA512)) { derived = pbkdf2.GetBytes(HashByteSize); } return string.Format("{0}:{1}:{2}", Iterations, Convert.ToBase64String(derived), Convert.ToBase64String(saltBytes)); } public static bool VerifyPasswordHash(string password, string hash) { try { string[] parts = hash.Split(new char[] { ':' }); byte[] saltBytes = Convert.FromBase64String(parts[2]); byte[] derived; int iterations = Convert.ToInt32(parts[0]); using (var pbkdf2 = new Rfc2898DeriveBytes( password, saltBytes, iterations, HashAlgorithmName.SHA512)) { derived = pbkdf2.GetBytes(HashByteSize); } string new_hash = string.Format("{0}:{1}:{2}", Iterations, Convert.ToBase64String(derived), Convert.ToBase64String(saltBytes)); return hash == new_hash; } catch { return false; } } } }

注意事項

pbkdf2 使用的是 Rfc 2898 規範,他的套件是Rfc2898DeriveBytes,雖然最小支援度是寫dotnet Framework 4.6,但要升級到dotnet Framework 4.7.2以上才能在直接寫SHA-512這種方式挑選要採用的雜湊演算法

PBKDF2 破解的可能性探討

參考 https://kknews.cc/zh-tw/tech/aeoml8j.html

PBKDF2 確實有很大的效果,但對於硬體破解,卻無任何對抗措施。

因為 PBKDF2 只是對原函數簡單封裝,多執行幾次而已。如果原函數不能對抗硬體,那麼套一層 PBKDF2 同樣也不能。

儘管單次 PBKDF 不能被拆解,但可以要求多次 PBKDF,並且互相沒有依賴。這樣多線程就能派上用場了。
例如我們對 PBKDF 進行封裝,要求執行 4 次完全獨立的計算,最後再將結果融合到一起:

function Parall(Password, Salt, ...)

    -- 該部分可被並行 --
    for i = 0 .. 4
        DK[i] = PBKDF(Password, Salt + i, ...)
    ------------------

    return Hash(DK)

這樣,我們即可開啟 4 個線程,同時計算這 4 個 PBKDF。

現在就能用 1 秒的時間,獲得之前 4 秒的強度!攻擊者破解時,成本就增加了 4 倍。

其他更能對抗硬體加密的演算法

bcrypt
scrypt
Argon2

PBKDF2加密

https://blog.csdn.net/chenfanglincfl/article/details/46994393

比較小的pbkdf2範例

https://riptutorial.com/zh-TW/csharp/example/10258/密碼哈希的pbkdf2

內含各種演算法破解速度比較圖表
https://blackie1019.github.io/2019/02/21/Secured-Password-with-PBKDF2-on-C/

aes可逆加密演算法
https://www.cnblogs.com/zxbzl/archive/2013/03/04/2942939.html

tags: 資安