PHP 5.5.0 引入 Password Hashing API,提供 `password_hash()`、`password_verify()`、`password_needs_rehash()`、`password_get_info()` 四個函數,基於 bcrypt 預設算法,內建鹽值生成、成本因子控制,是當前 PHP 存儲密碼的標準方式。
<?php
$password = 'user_password_123';
// 預設用法 (bcrypt, cost=10)
$hash = password_hash($password, PASSWORD_BCRYPT);
// 結果示例: $2y$10$uOegXJ09qznQsKvPfxr61uWjpJBxVDH2KGJQVnodzjnglhs2WTwHu
// 自定義選項
$options = [
'cost' => 12, // 成本因子,越高越慢越安全,建議 10-12
'salt' => random_bytes(22) // PHP 7+ 建議讓函數自動生成,手動指定鹽值已棄用
];
$hash = password_hash($password, PASSWORD_BCRYPT, $options);
// 結果示例: $2y$12$JDJ5JDEwJDhsTHV6SGVIQuprRHZnGQsUEtlk8Iem0okH6HPyCoo22
?>
重要:PHP 7.0+ `salt` 選項已棄用,由內部使用 `random_bytes()` 自動生成安全鹽值。`cost` 因硬件性能而異,建議測試單次哈希耗時 100-300ms。
<?php
$info = password_get_info($hash);
/*
array(3) {
["algo"]=> int(1) // 算法常量 PASSWORD_BCRYPT
["algoName"]=> string(6) "bcrypt"
["options"]=> array(1) {
["cost"]=> int(12)
}
}
*/
?>
<?php
// 例如:系統升級,cost 從 10 提高到 12
if (password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 12])) {
// 用戶下次登錄驗證成功後,重新生成哈希並更新數據庫
$newHash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
// UPDATE users SET password = $newHash WHERE id = $uid
}
?>
適用場景:算法升級、成本因子調整、鹽值策略變更。無需強制所有用戶重置密碼,漸進式遷移。
<?php
$hash = '$2y$10$JDJ5JDEwJDhsTHV6SGVIQuprRHZnGQsUEtlk8Iem0okH6HPyCoo22';
$auth = password_verify('user_password_123', $hash); // true
$auth = password_verify('wrong_password', $hash); // false
// 完整登錄驗證流程
$stmt = $db->prepare("SELECT password FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user && password_verify($inputPassword, $user['password'])) {
// 登錄成功
if (password_needs_rehash($user['password'], PASSWORD_BCRYPT, ['cost' => 12])) {
$newHash = password_hash($inputPassword, PASSWORD_BCRYPT, ['cost' => 12]);
$db->prepare("UPDATE users SET password = ? WHERE email = ?")->execute([$newHash, $email]);
}
// 設置會話...
}
?>
| 常量 | 算法 | 說明 |
|---|---|---|
| PASSWORD_BCRYPT | BCRYPT | 預設,支援 cost 參數,結果長度 60 字符 |
| PASSWORD_ARGON2I | Argon2i | PHP 7.2+,抗側信道攻擊,支援 memory_cost/time_cost/threads |
| PASSWORD_ARGON2ID | Argon2id | PHP 7.3+,綜合 Argon2i/d 優點,推薦新項目使用 |
| PASSWORD_DEFAULT | 當前預設 | PHP 版本升級時可能變更 (現為 BCRYPT),長度可能變化,字段建議 VARCHAR(255) |
<?php
// 登錄時檢測舊哈希格式
if (strlen($row['password']) === 32 && ctype_xdigit($row['password'])) {
// 疑似 MD5
if (md5($inputPassword) === $row['password']) {
// 驗證成功,升級為新哈希
$newHash = password_hash($inputPassword, PASSWORD_DEFAULT);
$db->prepare("UPDATE users SET password = ? WHERE id = ?")->execute([$newHash, $row['id']]);
// 登錄成功...
}
} elseif (password_verify($inputPassword, $row['password'])) {
// 已是新哈希,正常驗證
// ...
}
?>
Password Hashing API 解決了密碼存儲的核心難題:算法選擇、鹽值管理、成本控制、平滑升級。所有新項目應直接使用,舊項目應制定遷移計劃。安全無小事,密碼哈希是防禦數據洩露的最後一道防線。