在PHP 中,用戶認證是構建安全應用的基礎環節。比較密碼或令牌時,如果採用不安全的字符串比較方法,可能會導致時間攻擊(Timing Attack),從而讓攻擊者推斷出敏感信息。 PHP 提供了一個專門用於防止這類攻擊的函數hash_equals() ,能夠以恆定時間的方式比較兩個字符串,確保安全性。
本文將介紹如何利用hash_equals()函數實現安全的用戶認證流程,避免常見的安全隱患。
時間攻擊是指攻擊者通過測量程序對不同輸入字符串進行比較所需時間的差異,逐步猜測出敏感信息,比如密碼哈希或身份驗證令牌。
傳統的字符串比較函數(如==或strcmp )在匹配失敗時會儘早返回,導致比較時間依賴於字符串的相似度,從而暴露信息。
hash_equals()是PHP 5.6 及以上版本提供的函數,設計用於對兩個字符串進行恆定時間的比較,避免因返回時間差異導致的信息洩露。
函數原型:
bool hash_equals(string $known_string, string $user_string)
$known_string :已知的字符串(通常是存儲的哈希值)。
$user_string :用戶輸入的字符串(需要驗證的值)。
如果兩個字符串相等,返回true ,否則返回false 。
假設用戶註冊時,我們將密碼使用安全算法哈希後存儲,登錄時我們需要驗證用戶輸入的密碼。
示例代碼:
<?php
// 用戶註冊時,將密碼哈希存儲到數據庫
$password = 'user_password123';
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// 用戶登錄時,驗證密碼
$input_password = $_POST['password'] ?? '';
// 驗證密碼
if (password_verify($input_password, $hashed_password)) {
// 取出數據庫中保存的哈希字符串
$stored_hash = $hashed_password;
// 將用戶輸入密碼重新哈希用於比較(這裡示範場景為驗證令牌等情況)
$input_hash = hash('sha256', $input_password);
// 使用 hash_equals 進行恆定時間比較
if (hash_equals($stored_hash, $input_hash)) {
echo "認證成功";
} else {
echo "認證失敗";
}
} else {
echo "認證失敗";
}
?>
注意:上述示例中password_verify()本身已安全驗證密碼哈希, hash_equals()適用於驗證類似API 令牌或自定義哈希值的場景。
在許多場景中,我們需要驗證用戶傳遞的API 令牌是否匹配服務器端存儲的令牌,若直接用==比較存在時間攻擊風險。
示例:
<?php
// 服務器端存儲的API令牌
$stored_token = 'abc123def456';
// 用戶提交的令牌
$provided_token = $_GET['token'] ?? '';
// 進行安全比較
if (hash_equals($stored_token, $provided_token)) {
echo "令牌驗證通過";
} else {
echo "令牌驗證失敗";
}
?>
這樣即使令牌不匹配,攻擊者也無法通過比較耗時推斷出正確的令牌。
hash_equals()提供了防止時間攻擊的恆定時間字符串比較。
在密碼驗證中,推薦使用password_hash()與password_verify() , hash_equals()適用於令牌或哈希值的安全比較。
通過合理使用hash_equals() ,可以顯著提升認證過程的安全性,減少敏感信息洩露的風險。
如果你想了解更多關於PHP 安全認證的最佳實踐,可以訪問https://gitbox.net/php-security 。