Discuz! 經典 authcode($string, $operation, $key, $expiry) 是一個基於 RC4 流加密演算法變體的對稱加密函數,廣泛用於 Cookie 加密、表單令牌、API 簽名。本文逐行解析其核心機制。
$authkey = ''; // 全域預設金鑰
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
global $authkey;
$ckey_length = 4; // 動態金鑰長度
// 1. 金鑰派生
$key = md5($key ? $key : $authkey); // 32 字元
$keya = md5(substr($key, 0, 16)); // 加密核心金鑰
$keyb = md5(substr($key, 16, 16)); // 數據完整性校驗金鑰
$keyc = $ckey_length
? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length))
: '';
$cryptkey = $keya . md5($keya . $keyc); // 最終加密金鑰(含動態鹽)
$key_length = strlen($cryptkey); // 64 字元
// 2. 數據預處理
if ($operation == 'DECODE') {
$string = base64_decode(substr($string, $ckey_length));
} else {
// 編碼格式:10位時間戳 + 16位完整性校驗 + 原文
$string = sprintf('%010d', $expiry ? $expiry + time() : 0)
. substr(md5($string . $keyb), 0, 16)
. $string;
}
$string_length = strlen($string);
// 3. RC4 核心:密鑰調度演算法 (KSA)
$box = range(0, 255);
$rndkey = [];
for ($i = 0; $i <= 255; $i++) $rndkey[$i] = ord($cryptkey[$i % $key_length]);
for ($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i]; $box[$i] = $box[$j]; $box[$j] = $tmp;
}
// 4. RC4 核心:偽隨機生成演算法 (PRGA) + 異或加密
$result = '';
for ($a = $j = $i = 0; $i < $string_length; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a]; $box[$a] = $box[$j]; $box[$j] = $tmp;
$result .= chr(ord($string[$i]) ^ $box[($box[$a] + $box[$j]) % 256]);
}
// 5. 解碼驗證
if ($operation == 'DECODE') {
// 檢查:時間戳未過期 + 完整性校驗通過
if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0)
&& substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
return substr($result, 26); // 返回原文
}
return ''; // 驗證失敗
} else {
// 編碼輸出:動態金鑰 + Base64(密文) 移除 =
return $keyc . str_replace('=', '', base64_encode($result));
}
}
// 測試
$str = '1234'; $key = '1234';
$encode = authcode($str, 'ENCODE', $key);
$decode = authcode($encode, 'DECODE', $key);
// 輸出:明文:1234 / 密文:a52f67eXZGVy0HtQBo4vOREhq3WxnL6E2zlx75JGfoJW / 解密:1234
| 環節 | 關鍵代碼 | 安全作用 |
|---|---|---|
| 動態鹽 | $keyc = substr(md5(microtime()), -4) | 相同明文每次加密結果不同,防彩虹表 |
| 時間戳嵌入 | sprintf('%010d', $expiry+time()) | 支援過期時間,防重放攻擊 |
| 完整性校驗 | substr(md5($string.$keyb),0,16) | 防篡改,解密時驗證 |
| RC4 變體 | $box[($box[$a]+$box[$j])%256] | 雙重 S-box 查找增加分析難度 |
| Base64 輸出 | str_replace('=', '', base64_encode()) | 安全傳輸,移除填充字元 |
// PHP 7.2+ sodium 擴展(推薦)
function encrypt_modern($data, $key) {
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$cipher = sodium_crypto_secretbox($data, $nonce, $key);
return $nonce . $cipher; // 儲存 nonce + 密文
}
function decrypt_modern($payload, $key) {
$nonce = substr($payload, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$cipher = substr($payload, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
return sodium_crypto_secretbox_open($cipher, $nonce, $key);
}
// 金鑰生成:$key = sodium_crypto_secretbox_keygen();
authcode() 是 PHP 早期「自行實現加密」的縮影,理解其設計思路與缺陷,是避免重複造輪子、正確選用現代密碼學庫的關鍵一課。