首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 加密解密實戰:從 Discuz! authcode 到現代 AES-GCM 最佳實踐

     編輯:PHP 相關知識

經典:Discuz! authcode 算法分析

Discuz! 的 authcode() 採用自定義 XOR + Base64 + 動態密鑰機制,雖然曾廣泛使用,但不符合現代密碼學標準,存在已知弱點,新項目請勿使用。

核心流程:

  1. 密鑰派生:MD5($key) → 分為 $keya, $keyb, $keyc。
  2. 動態密鑰 $keyc 加密時隨機,解密時從密文首部提取。
  3. RC4 風格的 S-box 置換流加密。
  4. 明文結構:時間戳(10位) + 完整性校驗(16位) + 原文。
  5. Base64 編碼輸出,去除 =。

現代標準:OpenSSL AES-256-GCM (PHP 7.1+)

認證加密 (AEAD) 同時保證機密性、完整性、防重放。

<?php
/**
 * 現代加密工具類
 */
class Crypto {
    const CIPHER = 'aes-256-gcm';
    const KEY_LEN = 32;   // 256 bit
    const IV_LEN = 12;    // 96 bit for GCM
    const TAG_LEN = 16;

    /** 從密碼派生金鑰 (PBKDF2) */
    public static function deriveKey(string $password, string $salt): string {
        return hash_pbkdf2('sha256', $password, $salt, 100_000, self::KEY_LEN, true);
    }

    /** 加密:返回 Base64(salt + iv + ciphertext + tag) */
    public static function encrypt(string $data, string $password): string {
        $salt = random_bytes(16);
        $key = self::deriveKey($password, $salt);
        $iv = random_bytes(self::IV_LEN);
        
        $ciphertext = openssl_encrypt($data, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv, $tag);
        if ($ciphertext === false) throw new \Exception('加密失敗');
        
        // 組合:salt(16) + iv(12) + ciphertext + tag(16)
        return base64_encode($salt . $iv . $ciphertext . $tag);
    }

    /** 解密 */
    public static function decrypt(string $payload, string $password): string {
        $raw = base64_decode($payload, true);
        if ($raw === false) throw new \Exception('Base64 解碼失敗');
        
        $salt = substr($raw, 0, 16);
        $iv = substr($raw, 16, 12);
        $tag = substr($raw, -16);
        $ciphertext = substr($raw, 28, -16);
        
        $key = self::deriveKey($password, $salt);
        $plaintext = openssl_decrypt($ciphertext, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv, $tag);
        if ($plaintext === false) throw new \Exception('解密失敗:數據被篡改或密碼錯誤');
        
        return $plaintext;
    }
}

// 使用示例
$pwd = 'MySuperSecretPassword2024!';
$token = Crypto::encrypt('敏感數據:用戶ID=12345', $pwd);
echo "加密: $token\n";
echo "解密: " . Crypto::decrypt($token, $pwd) . "\n";

場景選型指南

場景推薦方案備註
API Token、Session IDJWT (RS256) / 隨機字符串 + 資料庫無需對稱加密
用戶隱私字段落地加密AES-256-GCM金鑰管理用 Vault/KMS
密碼存儲bcrypt / Argon2id單向哈希,絕不可逆
舊系統兼容authcode (僅解密)逐步遷移至新方案

金鑰管理鐵律

  1. 代碼庫絕不硬編碼金鑰,使用環境變量或密鑰管理服務。
  2. 定期輪換金鑰,舊數據重新加密。
  3. 區分環境:開發/測試/生產使用獨立金鑰。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved