function authcode(string $string, string $operation = 'DECODE', string $key = '', int $expiry = 0): string
$string:明文(加密時)或密文(解密時)。$operation:ENCODE 加密、DECODE 解密。$key:自訂金鑰,留空則用 $GLOBALS['discuz_auth_key'](Discuz 設定檔常數)。$expiry:有效期秒數(加密時),0 表永久。解密時會驗證是否過期。$key = md5($key ?: $GLOBALS['discuz_auth_key']) → 32 字元十六進位。
$keya = md5(substr($key, 0, 16)):參與加解密運算(16 字元)。$keyb = md5(substr($key, 16, 16)):資料完整性驗證(16 字元)。$keyc:動態金鑰(4 字元預設),加密時取 md5(microtime()) 末 4 碼,解密時從密文前 4 碼取出。**這是相同明文每次產生不同密文的關鍵。**$cryptkey = $keya . md5($keya . $keyc)(長度 32+32=64 字元)。$plain = sprintf('%010d', $expiry ? $expiry + time() : 0) // 10 位數時間戳(過期檢查)
. substr(md5($string . $keyb), 0, 16) // 16 字元完整性檢查碼
. $string; // 原始明文
$box) 並用 $cryptkey 打亂(KSA 階段)。$result .= chr(ord($plain[$i]) ^ $box[($box[$a] + $box[$j]) % 256])(PRGA 階段)。$keyc . str_replace('=', '', base64_encode($result))。前綴動態金鑰、Base64 編碼移除填充 =。$keyc,重建 $cryptkey。substr($decrypted, 0, 10) == 0 || ... - time() > 0(未過期)。substr($decrypted, 10, 16) == substr(md5(substr($decrypted, 26) . $keyb), 0, 16)。substr($decrypted, 26)(原始明文),否則回傳空字串。| 項目 | 評估 | 風險 |
|---|---|---|
| 演算法 | 自訂 RC4 變體 | 高:RC4 已知偏差攻擊、密鑰排程弱點,不建議新系統使用 |
| 金鑰長度 | 實際加密金鑰 256 bits (md5x2) | 中:理論足夠,但 RC4 弱化有效強度 |
| 動態 IV | $keyc 4 字元 (32 bits) | 高:空間極小 (16^4=65536),極易重複,導致密文重複攻擊 |
| 完整性保護 | MD5 截斷 16 字元 (64 bits) | 中:MD5 碰撞風險,截斷進一步降低強度 |
| 時間戳防重放 | 10 位數秒級 | 低:秒級精度無法防禦同秒重放 |
| Base64 編碼 | 標準 | 無風險 |
$keyc 只有 4 字元,大量加密極易產生相同 $keyc,導致相同明文產生相同密文核心部分,洩露資訊。$GLOBALS['discuz_auth_key'] 保護全站所有加密資料,一旦洩露全盤皆駭。// 使用 PHP 內建 sodium 擴展(PHP 7.2+)或 openssl
function modernEncrypt(string $data, string $key): string {
// $key 必須為 32 位元組 (256 bits),可用 hash('sha256', $masterKey, true) 衍生
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); // 24 bytes
$cipher = sodium_crypto_secretbox($data, $nonce, $key);
return base64_encode($nonce . $cipher);
}
function modernDecrypt(string $payload, string $key): string|false {
$raw = base64_decode($payload, true);
if ($raw === false) return false;
$nonce = substr($raw, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$cipher = substr($raw, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
return sodium_crypto_secretbox_open($cipher, $nonce, $key);
}
Discuz authcode 是早期 PHP 社群廣泛使用的經典加密函式,設計巧妙(動態 IV、時間戳、完整性碼、RC4 流式),但在現代密碼學標準下**不再安全**,存在 IV 空間過小、RC4 弱點、MD5 碰撞、缺乏 AEAD 等嚴重缺陷。**新專案請務必使用 libsodium (sodium_*) 或 OpenSSL AES-GC/ChaCha20-Poly1305**,並妥善管理金鑰生命週期。維護舊系統時,若無法立即替換,至少增加傳輸層 TLS、限制加密資料敏感度、監控異常解密失敗。