PHP 5.5 引入了 Password Hashing API,提供 password_hash()、password_verify()、password_needs_rehash() 等函數,徹底解決了早期 MD5/SHA1 易受彩虹表攻擊、手動管理 Salt 繁瑣等問題。本文深度解析這套 API 的正確使用姿勢。
| 函數 | 用途 | 關鍵特性 |
|---|---|---|
password_hash() | 創建密碼哈希 | 自動生成 Salt、內置 Cost 因子、算法可升級 |
password_verify() | 驗證密碼 | 恆定時間比較,防時序攻擊 |
password_needs_rehash() | 檢查是否需重哈希 | 算法升級、Cost 調整時自動遷移 |
password_get_info() | 獲取哈希元信息 | 解析算法、參數,無需存儲額外字段 |
<?php
$password = 'user_password_123';
// 最簡單用法:默認 BCRYPT、Cost=10、自動 Salt
$hash = password_hash($password, PASSWORD_BCRYPT);
// 結果示例:$2y$10$uOegXJ09qznQsKvPfxr61uWjpJBxVDH2KGJQVnodzjnglhs2WTwHu
// 進階:自定義 Cost 與 Salt(通常不建議手動指定 Salt)
$options = [
'cost' => 12, // 更高安全性,CPU 開銷指數級增長
'salt' => random_bytes(22) // PHP 7+ 建議用 random_bytes,PHP 5.5 用 mcrypt_create_iv
];
$hash = password_hash($password, PASSWORD_BCRYPT, $options);
// 結果示例:$2y$12$JDJ5JDEwJDhsTHV6SGVIQuprRHZnGQsUEtlk8Iem0okH6HPyCoo22
?>
最佳實踐:不要手動指定 Salt,讓 API 自動生成加密安全的隨機 Salt。Cost 值建議 10~12,根據服務器性能調整(驗證耗時 100~300ms 為宜)。
<?php
$info = password_get_info($hash);
/*
array(3) {
["algo"] => int(1) // 算法常量
["algoName"] => string(6) "bcrypt"
["options"] => array(1) { ["cost"] => int(12) }
}
*/
?>
無需在數據庫額外存儲算法版本、Cost 等信息,哈希字符串已自包含所有元數據。
當安全策略升級(如 Cost 從 10 提升到 12、或遷移至 Argon2)時,無需強制所有用戶重置密碼,僅在登錄時檢查並平滑遷移:
<?php
// 登錄驗證邏輯中
if (password_verify($inputPassword, $storedHash)) {
// 密碼正確,檢查是否需升級哈希
if (password_needs_rehash($storedHash, PASSWORD_BCRYPT, ['cost' => 12])) {
$newHash = password_hash($inputPassword, PASSWORD_BCRYPT, ['cost' => 12]);
// 更新數據庫存儲的哈希
updateUserHash($userId, $newHash);
}
// 登錄成功...
}
?>
<?php
$hash = '$2y$12$JDJ5JDEwJDhsTHV6SGVIQuprRHZnGQsUEtlk8Iem0okH6HPyCoo22';
var_dump(password_verify('user_password_123', $hash)); // true
var_dump(password_verify('wrong_password', $hash)); // false
?>
password_verify() 內部使用恆定時間比較,有效防禦時序側信道攻擊。
password_hash 字段(VARCHAR 255),保留舊哈希字段做回滾。password_hash() 重新生成並更新數據庫,清空舊字段。Password Hashing API 讓密碼存儲變得簡單而安全,請立即在項目中替換所有手動哈希邏輯。