在使用PHP 的crypt()函數進行密碼加密時,不少開發者會注意到一個奇怪的現象:有時候加密結果長度是13 個字符,有時候是20、34,甚至更多。這種長度的不一致容易讓人誤以為代碼出了問題,或是密碼被截斷或加密錯誤。那麼,這到底是怎麼回事?
PHP 的crypt()函數是一個封裝的加密接口,底層依賴於操作系統提供的加密算法。這個函數的第一個參數是要加密的字符串,第二個參數是“鹽值”(salt)。而這個鹽值不僅影響加密結果的唯一性,更決定了使用哪種加密算法。
echo crypt("mypassword", "salt");
如果你不給出鹽值或使用的鹽值格式不正確,PHP 會自動退回到較舊的加密方式,比如傳統的DES(Data Encryption Standard)算法,而這就會導致輸出結果非常短,通常只有13 個字符。
crypt()函數支持多種算法,不同算法通過鹽值的前綴來決定。下面是常見的幾種鹽值格式及其對應的加密算法:
演算法 | 鹽值格式 | 結果長度 |
---|---|---|
DES | 2個字符 | 13字符 |
MD5 | $1$... | 34字符 |
Blowfish | $2a$... / $2y$... | 60字符 |
SHA-256 | $5$... | 43字符 |
SHA-512 | $6$... | 86字符 |
所以如果你沒有顯式地傳入帶前綴的鹽值,或傳入了不符合格式的鹽值,PHP 很可能退回使用舊的DES 加密,而DES 只輸出13 個字符。
例如:
// 使用 SHA-512 加密
echo crypt("mypassword", '$6$rounds=5000$usesomesillystring$');
// 輸出類似於:$6$rounds=5000$usesomesill...(總長度約86)
而以下代碼使用默認DES 算法:
echo crypt("mypassword", 'sa');
// 輸出類似於:sahGDf/YwKdl6(僅13個字符)
為避免這個問題,請始終使用帶有明確前綴的鹽值,最好是生成的隨機鹽,並且選用更安全的算法,如Blowfish( $2y$ )或SHA-512( $6$ )。
你可以這樣自動生成鹽值:
function generateSalt($algo = 'sha512') {
switch ($algo) {
case 'blowfish':
return '$2y$10$' . substr(str_replace('+', '.', base64_encode(random_bytes(22))), 0, 22);
case 'sha256':
return '$5$' . bin2hex(random_bytes(6));
case 'sha512':
default:
return '$6$' . bin2hex(random_bytes(6));
}
}
$password = 'mypassword';
$salt = generateSalt('sha512');
$hash = crypt($password, $salt);
echo $hash;
<?php
function hashPassword($password) {
$salt = '$2y$10$' . substr(str_replace('+', '.', base64_encode(random_bytes(22))), 0, 22);
return crypt($password, $salt);
}
$plainPassword = '123456';
$hashed = hashPassword($plainPassword);
// 假設保存到數據庫中
file_put_contents('/var/www/m66.net/passwords.txt', $hashed . PHP_EOL);
// 驗證密碼
$inputPassword = '123456';
$isValid = crypt($inputPassword, $hashed) === $hashed;
echo $isValid ? '密碼正確' : '密碼錯誤';
?>
使用crypt()函數時,結果長度不同的根本原因在於“鹽值”的格式決定了加密算法。為確保加密安全和一致性:
始終顯式傳入正確格式的鹽值;
選擇現代、安全的算法,如$2y$或$6$ ;
不要依賴默認行為或短鹽值格式。
理解這些底層細節後,就可以避免在密碼處理上出現隱蔽的安全風險,也能讓你的加密邏輯更可控、更可靠。