PHP 5.5.0 引入 Password Hashing API,提供安全、簡單的密碼雜湊與驗證機制,取代手動 md5()/sha1() + 鹽值的錯誤做法。核心四函式:
password_hash() —— 建立雜湊password_verify() —— 驗證密碼password_get_info() —— 取得雜湊資訊password_needs_rehash() —— 檢查是否需重新雜湊$password = 'foo'; $hash = password_hash($password, PASSWORD_BCRYPT); // 結果範例:$2y$10$uOegXJ09qznQsKvPfxr61uWjpJBxVDH2KGJQVnodzjnglhs2WTwHu
支援演算法常數:PASSWORD_BCRYPT(預設)、PASSWORD_ARGON2I、PASSWORD_ARGON2ID(PHP 7.2+)。
$options = [
'cost' => 12, // BCRYPT 成本因子,預設 10,愈大愈慢愈安全
'salt' => random_bytes(16), // PHP 7.0+ 建議省略,讓函式自動產生
];
$hash = password_hash($password, PASSWORD_BCRYPT, $options);
注意: PHP 7.0 起 salt 選項已棄用,請讓 API 自行處理。
var_dump(password_get_info($hash));
/*
array(3) {
["algo"]=> int(1) // 演算法代碼
["algoName"]=> string(6) "bcrypt"
["options"]=> array(1) { ["cost"]=> int(12) }
}
*/
當你想提高 cost 或更換演算法時,可在登入驗證成功後呼叫:
if (password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 12])) {
$newHash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
// 更新資料庫儲存的 $newHash
}
此函式不需驗證密碼,僅依演算法與選項判斷。
$hash = '$2y$10$JDJ5JDEwJDhsTHV6SGVIQuprRHZnGQsUEtlk8Iem0okH6HPyCoo22';
var_dump(password_verify('foo', $hash)); // true
var_dump(password_verify('bar', $hash)); // false
回傳 bool,內部採用恆定時間比較,防止時序攻擊。
// 註冊
function register($username, $password) {
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
// INSERT INTO users (username, password_hash) VALUES (?, ?)
}
// 登入
function login($username, $password) {
// SELECT password_hash FROM users WHERE username = ?
$hash = $row['password_hash'];
if (password_verify($password, $hash)) {
if (password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 12])) {
$newHash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
// UPDATE users SET password_hash = ? WHERE username = ?
}
return true; // 登入成功
}
return false;
}
| 誤區 | 正確做法 |
|---|---|
| 自行產生鹽值 | 讓 password_hash() 內建處理 |
| 使用 MD5/SHA1 | 一律改用 Password Hashing API |
| 將雜湊存為 CHAR(32) | 欄位長度至少 VARCHAR(255) 預留未來演算法 |
雙重雜湊 md5(md5($pwd)) | 無意義,反而降低安全性 |