在構建用戶認證系統時,安全地處理密碼是開發者必須重視的問題。錯誤地比較密碼,可能導致系統受到時序攻擊(Timing Attacks)等安全威脅。 PHP 提供了一些內建函數,可以幫助我們安全地處理密碼驗證。其中, crypt()和hash_equals()的組合使用,是一種相對安全的實踐方式。
crypt()是PHP 的一個加密函數,用於基於某種算法(如bcrypt 、 SHA-512等)對密碼進行哈希加密。它的語法如下:
string crypt(string $string, string $salt)
salt參數決定了加密算法及其行為。對於現代應用,推薦使用bcrypt ,可以通過以下方式自動生成適當的salt :
$salt = '$2y$10$' . substr(strtr(base64_encode(random_bytes(16)), '+', '.'), 0, 22);
$hash = crypt('mypassword', $salt);
很多初學者會寫出如下代碼來驗證密碼:
if (crypt($inputPassword, $storedHash) == $storedHash) {
// 登錄成功
}
這在功能上看似可行,但存在嚴重的安全隱患——時序攻擊(Timing Attack) 。攻擊者可以通過測量程序響應時間的微小差異,逐步猜出正確的密碼哈希。
為了解決時序攻擊的問題,PHP 從5.6 開始提供了hash_equals()函數,用於安全地比較兩個哈希值:
bool hash_equals(string $known_string, string $user_string)
它使用恆定時間算法,不會根據字符串內容提前返回,避免了時間差洩露。
<?php
// 假設我們從數據庫獲取了加密後的密碼哈希
$storedHash = '$2y$10$9YzyYtVht3tcGEn.7PiF2OlRM0HDTrM7Z5D.yPi8hdm0fJeFVKH4K'; // 由 crypt() 生成的 bcrypt 哈希
$inputPassword = $_POST['password'] ?? '';
// 用 crypt() 對用戶輸入的密碼進行哈希,使用存儲哈希作為 salt
$inputHash = crypt($inputPassword, $storedHash);
// 使用 hash_equals() 安全比較
if (hash_equals($storedHash, $inputHash)) {
echo "登錄成功!";
} else {
echo "密碼錯誤!";
}
?>
雖然crypt()仍然可以安全使用,但PHP 官方更推薦使用password_hash()和password_verify() ,因為它們封裝了算法選擇與安全比較過程,使用更加簡單安全。例如:
// 創建密碼哈希
$hash = password_hash('mypassword', PASSWORD_BCRYPT);
// 驗證密碼
if (password_verify($inputPassword, $hash)) {
echo "驗證通過";
}
但在某些場景下(如維護舊系統或有特殊需求), crypt()與hash_equals()的組合仍然是一個可接受且安全的方案。
避免使用==或===比較密碼哈希;
使用crypt()哈希密碼時要正確選擇salt和算法(推薦bcrypt);
使用hash_equals()進行安全比較,防止時序攻擊;
如果沒有特定理由,推薦使用password_hash()和password_verify()替代低層封裝。
在處理用戶密碼時,安全不是可選項,而是必需項。正確的做法不僅能保護用戶隱私,還能提升整個系統的抗攻擊能力。