PHP 7+ 提供密碼學安全的隨機源:**嚴禁在安全相關場景使用 `rand()`、`mt_rand()`、`shuffle()`、`uniqid()`、時間戳**。
<?php
/**
* 生成加密安全的隨機密碼
* @param int $length 密碼長度 (建議 12-32)
* @param string $charset 字符集 (默認含大小寫字母、數字、符號)
* @param bool $requireAll 是否強制包含各類字符至少一個
* @return string
*/
function generateSecurePassword(
int $length = 16,
string $charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_-+=',
bool $requireAll = true
): string {
if ($length < 8) throw new InvalidArgumentException('Length must be >= 8');
$charsetLen = strlen($charset);
$bytes = random_bytes($length * 2); // 獲取足夠熵
$password = '';
$hasLower = $hasUpper = $hasDigit = $hasSymbol = false;
$symbolSet = '!@#$%^&*_-+=';
for ($i = 0; $i < $length; $i++) {
// 無偏差採樣:拒絕採樣法
$maxValid = (256 / $charsetLen) * $charsetLen - 1;
do {
$byte = $bytes[$i * 2] | ($bytes[$i * 2 + 1] << 8);
} while ($byte > $maxValid);
$char = $charset[$byte % $charsetLen];
$password .= $char;
if (ctype_lower($char)) $hasLower = true;
elseif (ctype_upper($char)) $hasUpper = true;
elseif (ctype_digit($char)) $hasDigit = true;
elseif (str_contains($symbolSet, $char)) $hasSymbol = true;
}
// 強制包含各類字符
if ($requireAll && !($hasLower && $hasUpper && $hasDigit && $hasSymbol)) {
return generateSecurePassword($length, $charset, $requireAll); // 遞歸重試 (極少發生)
}
return $password;
}
// 使用
echo generateSecurePassword(16); // 默認字符集
echo generateSecurePassword(20, 'abcdefghijklmnopqrstuvwxyz0123456789'); // 易輸入字符集
echo generateSecurePassword(32, '0123456789abcdef'); // 十六進制密鑰
function generatePassphrase(int $words = 4, string $separator = '-'): string {
// 內建 EFF 大詞表精簡版 (實際用完整 7776 詞)
$wordlist = ['apple','banana','cherry','dolphin','eagle','forest','guitar','horizon','island','journey',
'kangaroo','lemon','mountain','nebula','ocean','planet','quantum','river','sunset','tiger',
'umbrella','valley','waterfall','xenon','yacht','zebra'];
$count = count($wordlist);
$bytes = random_bytes($words * 2);
$pass = [];
for ($i = 0; $i < $words; $i++) {
$idx = ($bytes[$i*2] | ($bytes[$i*2+1] << 8)) % $count;
$pass[] = $wordlist[$idx];
}
return implode($separator, $pass);
}
echo generatePassphrase(5); // eagle-horizon-kangaroo-ocean-tiger
function passwordEntropy(string $password, int $charsetSize): float {
return strlen($password) * log($charsetSize, 2); // bits
}
function passwordStrength(float $entropy): string {
return match(true) {
$entropy < 30 => 'Very Weak',
$entropy < 60 => 'Weak',
$entropy < 80 => 'Fair',
$entropy < 100 => 'Strong',
default => 'Very Strong',
};
}
$pwd = generateSecurePassword(16);
echo "Entropy: " . passwordEntropy($pwd, 72) . " bits - " . passwordStrength(passwordEntropy($pwd, 72)) . "\n";
| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
| `substr(str_shuffle($charset), 0, $len)` | `str_shuffle` 使用 `mt_rand`,非加密安全 | `random_bytes` + 拒絕採樣 |
| `$charset[mt_rand(0, $len-1)]` | 模運算偏差 + `mt_rand` 可預測 | 拒絕採樣法 |
| `uniqid('', true)` | 基於微秒時間,熵極低,可暴力枚舉 | 永不用於密鑰/密碼 |
| `md5(time().rand())` | 時間+偽隨機,完全不可用 | 同上 |