PBKDF2 函式,比 “Hash 加鹽” 更好的口令保護方案
在前面兩篇文章中,對使用者口令進行加密的方式其實稱為 Password-based encryption (PBE),演算法實現很簡單,那是不是有更好和更標準的 PBE 實現呢?
基於 Hash+salt 的演算法最大的問題在於 Hash 函式的運算太快了,雖然加鹽讓暴力攻擊和彩虹表攻擊的可行性大大減低,但現在攻擊者能在非常快速的硬體(包括 GPU)上執行,如果時間足夠 ,還是有很大機率完成暴力破解。
那有沒有更好的解決方案嗎?如果讓口令運算運算的慢一點,那麼攻擊者破解的速度也將上不去,這樣是否就能更好的保護明文口令?
在密碼學中,key derivation function (KDF) 函式非常重要,它可以通過一個 master key(在 HTTPS 中用的非常多)、口令(password)、passphrase(密碼學隨機數生成器)生成一個或多個強壯的金鑰,這些金鑰本身被密碼學演算法使用(比如 AES、RSA 等等)。
使用者的口令通過 PBF(前兩篇文章講解的演算法實現)生成的口令密文不是金鑰,所以最終結果不是用於密碼學用途,是為了避免口令被破解,但本質上 BPF 演算法也可以通過 KDF 函式實現,也就是利用 KDF 函式生成口令密文。
KDF 同樣基於 Hash 函式,也有 salt 機制,當然最重要的是有迭代因子 這個概念,有了迭代因子,會讓處理速度變慢,減少爆破風險。
KDF 主要有三種實現,分別是PBKDF2、bcrypt、scrypt ,這篇文章主要討論 PBKDF2。
稍微休息下,希望大家理解上述概念之間的區別。
KDF 本質上屬於 Key stretching、key strengthening,如果你瞭解 HTTPS,那麼可能比較熟悉,比如在握手階段,HTTPS 將 Premaster Secret 和客戶端伺服器端的隨機數匯出為 Master Secret,然後再將 Master Secret 匯出為多個金鑰塊,這些金鑰塊包含 AES 的加密金鑰或者初始化向量,使用者後續通訊資料的加密和完整性保護。
PBE 演算法標準定義在 RFC 2898 文件中,大概的公式如下:
DK = PBKDF2(PRF, Password, Salt, c, dkLen)
-
PRF 是一個偽隨機函式,可以簡單的理解為 Hash 函式。
-
Password 表示口令 。
-
Salt 表示鹽值,一個隨機數。
-
c 表示迭代次數。
-
dkLen 表示最後輸出的金鑰長度。
如果 c 的數值越大,那麼運算速度就越慢,增加了時間複雜度,攻擊者破解的成功率就會下降。
使用 PHP 語言說明 PBKDF2 函式的使用:
$password = '明文口令'; // 隨機的鹽值 $salt = openssl_random_pseudo_bytes(12); $keyLength = 40; $iterations = 10000; $generated_key = openssl_pbkdf2($password, $salt, $keyLength, $iterations, 'sha256'); //轉換為 16 進位制 echo bin2hex($generated_key)."\n";
對這個過程迴圈2000次,總共需要16秒 ,而如果執行簡單的 Hash+salt,迴圈2000次,執行時間不到0.1秒 。
從這個角度看,建議大家使用 PBKDF2 保護你的口令,但現在業界的保護口令的標準演算法是 bcrypt ,下一篇文章會講解。
口令保護系列文章:
可以瞭解我的書《深入淺出HTTPS:從原理的實戰》 ,如果你覺得還不錯,還請在豆瓣上做個評論(地址:https://book.douban.com/subject/30250772/,或點選“原文連線”)。