在現代Web 開發中,用戶密碼的安全存儲是一項關鍵任務。彩虹表攻擊是一種常見的密碼破解技術,它依賴預計算的哈希值表來快速匹配原始密碼。為了有效防禦這種攻擊,我們必須為密碼哈希增加“鹽”(salt)。 PHP 的crypt()函數正是一個可以很好應對這一挑戰的工具,但前提是正確使用它。
PHP 的crypt()函數用於對字符串進行單向加密,常用於密碼哈希。它支持多種加密算法,包括傳統的DES、MD5,以及更安全的Blowfish( $2y$ )和SHA-256/512( $5$ / $6$ )等。
函數原型如下:
string crypt(string $string, string $salt);
其中$string是要加密的原始字符串, $salt決定了使用哪種算法以及加密的變體。
彩虹表攻擊依賴於對哈希值的預計算。然而,如果每次哈希的鹽值都不同,那麼攻擊者就無法使用通用的彩虹表進行匹配。 crypt()允許我們自定義鹽值,結合強加密算法就能有效避免這種攻擊。
舉例來說,我們可以這樣使用Blowfish 算法和動態鹽值:
$password = 'user-password';
$salt = '$2y$10$' . substr(strtr(base64_encode(random_bytes(16)), '+', '.'), 0, 22);
$hash = crypt($password, $salt);
在上面的代碼中:
$2y$10$表示使用Blowfish 算法,成本因子為10;
random_bytes(16)生成高強度隨機鹽;
substr(..., 0, 22)截取成符合Blowfish 鹽格式的長度。
這樣生成的$hash就是加鹽後的密碼哈希。
在用戶登錄時,我們不需要手動再次生成鹽,而是直接用存儲在數據庫中的哈希作為salt 參數傳入crypt() :
$inputPassword = 'user-input';
$storedHash = '$2y$10$qzWRzYy5q3DvYbEC0KMX4O0PGWlOQxkz7v0QF5OwMqpyG0TzjSAGK'; // 來自數據庫
if (hash_equals($storedHash, crypt($inputPassword, $storedHash))) {
echo '密碼正確';
} else {
echo '密碼錯誤';
}
這樣crypt()會自動使用哈希中的鹽和算法重新加密輸入密碼,再進行比較。這種方式簡潔且安全。
$hash = crypt($password, 'fixedsalt');
這種方式極易受到彩虹表攻擊,因為相同的密碼始終對應相同的哈希。
$hash = crypt($password, 'ab'); // DES 演算法,鹽太短
DES 不僅鹽短,而且計算成本低,現代硬件可以輕鬆暴力破解。
雖然crypt()是安全的,但PHP 從5.5 起推薦使用password_hash()和password_verify()這套更現代的密碼哈希API。不過,如果你正在維護老項目或出於兼容性考慮依然使用crypt() ,確保你做到以下幾點:
使用強算法(如$2y$ 、 $5$ 、 $6$ );
始終使用隨機且足夠長的鹽值;
永遠不要重複使用鹽;
鹽值應嵌入哈希中存儲,而不是單獨存儲。
// 註冊時
$password = $_POST['password'];
$salt = '$2y$12$' . substr(strtr(base64_encode(random_bytes(16)), '+', '.'), 0, 22);
$hash = crypt($password, $salt);
// 存儲 $hash 到數據庫中
// 登錄時
$inputPassword = $_POST['password'];
$storedHash = getHashFromDatabase(); // 從數據庫中獲取
if (hash_equals($storedHash, crypt($inputPassword, $storedHash))) {
echo '登錄成功';
} else {
echo '密碼錯誤';
}
你可以訪問https://m66.net/php-crypt-guide來獲取本教程的在線版本與更多示例。
正確使用crypt()函數可以有效防止彩虹表攻擊。關鍵在於使用強加密算法、隨機的鹽值,以及對比時的安全做法。雖然新項目推薦使用password_hash()系列函數,但掌握crypt()的安全用法同樣是每位PHP 開發者的必修課。