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

PHP 自定義可逆加密解密實作:XOR 流式加密、Base64 編碼、金鑰管理完整指南

     編輯:PHP 相關知識

前言:理解自定義加密的適用場景與風險

PHP 內建 openssl_encrypt、sodium_crypto_secretbox 等標準加密函數已足夠應對絕大多數場景。自定義加密 (如本文的 XOR 流式加密) 僅適合:**教學理解原理、低敏感度資料混淆、舊系統相容性維護**。**嚴禁** 用於金融交易、個資保護、醫療記錄等高價值資料。本文提供完整、可執行的自定義加密解密實作,並詳細說明其運作原理、安全性分析與生產環境替代方案。

核心演算法流程

  1. 金鑰派生:使用者金鑰 → MD5 → 32 字元十六進位金鑰 (KeyA)
  2. 隨機種子生成:加密時產生隨機金鑰 → MD5 → 32 字元 (KeyB),確保語意安全性
  3. 首輪 XOR 混合:明文逐字符與 KeyB XOR,並將 KeyB 字符穿插在結果中 (格式: K1⊕C1, K2⊕C2...)
  4. 次輪金鑰加密:使用 KeyA 對首輪結果進行 XOR 加密 (keyED 函數)
  5. 封裝輸出:Base64 編碼 + 首尾附加 MD5 校驗碼 (完整性保護)

完整可執行程式碼 (CryptoHelper 類別)

<?php
/**
 * PHP 自定義可逆加密解密工具類
 * 警告: 非標準加密演算法,僅供教學/低敏感度場景使用
 * 生產環境高敏感資料請使用 OpenSSL AES-GCM 或 Sodium
 */
class CryptoHelper {
    /**
     * 核心 XOR 運算 (加密/解密通用)
     * @param string $data 待處理資料
     * @param string $key  金鑰 (已經過 MD5)
     * @return string 處理後資料
     */
    private static function xorCrypt(string $data, string $key): string {
        $key = md5($key);
        $keyLen = strlen($key);
        $result = '';
        $ctr = 0;

        for ($i = 0; $i < strlen($data); $i++) {
            if ($ctr === $keyLen) $ctr = 0;
            $result .= $data[$i] ^ $key[$ctr];
            $ctr++;
        }
        return $result;
    }

    /**
     * 加密
     * @param string $plaintext 明文 (任意字串,含中文/二進位)
     * @param string $key       使用者金鑰 (建議 16+ 字元高熵值)
     * @return string 密文 (Base64,含完整性校驗)
     */
    public static function encrypt(string $plaintext, string $key): string {
        // 1. 生成隨機會話金鑰 (語意安全性)
        $sessionKey = md5((string)random_int(0, PHP_INT_MAX) . microtime(true));
        $sessionKeyLen = strlen($sessionKey);

        // 2. 首輪: 明文與會話金鑰 XOR,並穿插會話金鑰字符
        // 格式: [K1][C1⊕K1][K2][C2⊕K2]...
        $firstRound = '';
        $ctr = 0;
        for ($i = 0; $i < strlen($plaintext); $i++) {
            if ($ctr === $sessionKeyLen) $ctr = 0;
            $firstRound .= $sessionKey[$ctr] . ($plaintext[$i] ^ $sessionKey[$ctr]);
            $ctr++;
        }

        // 3. 次輪: 使用使用者金鑰加密首輪結果
        $secondRound = self::xorCrypt($firstRound, $key);

        // 4. 封裝: Base64 + 首尾 MD5 校驗碼
        $payload = base64_encode($secondRound);
        $integrity = md5($sessionKey) . $payload . md5($sessionKey . $key);

        return $integrity;
    }

    /**
     * 解密
     * @param string $ciphertext 密文
     * @param string $key        使用者金鑰 (需與加密時相同)
     * @return string 明文
     * @throws Exception 解密失敗 (格式錯誤/金鑰錯誤/資料損壞)
     */
    public static function decrypt(string $ciphertext, string $key): string {
        // 1. 格式檢查: 至少 64 字元 (32+32 MD5)
        if (strlen($ciphertext) < 64) {
            throw new Exception('密文格式錯誤:長度不足');
        }

        // 2. 移除首尾 MD5 校驗碼
        $core = substr($ciphertext, 32, -32);

        // 3. Base64 解碼 (嚴格模式)
        $decoded = base64_decode($core, true);
        if ($decoded === false) {
            throw new Exception('Base64 解碼失敗:資料可能已損壞');
        }

        // 4. 次輪解密: 使用使用者金鑰
        $firstRound = self::xorCrypt($decoded, $key);

        // 5. 還原明文: 解析 [K][C⊕K] 配對
        $plaintext = '';
        for ($i = 0; $i < strlen($firstRound); $i += 2) {
            if ($i + 1 >= strlen($firstRound)) {
                throw new Exception('密文格式錯誤:首輪資料長度異常');
            }
            $k = $firstRound[$i];       // 會話金鑰字符
            $xor = $firstRound[$i + 1]; // XOR 結果
            $plaintext .= $xor ^ $k;    // 還原: (C⊕K)⊕K = C
        }

        return $plaintext;
    }
}

// --- 立即測試 ---
if (php_sapi_name() === 'cli' || basename($_SERVER['SCRIPT_FILENAME']) === basename(__FILE__)) {
    header('Content-Type: text/html; charset=utf-8');

    $key = 'MySecretKey2024!@#$%^&*'; // 實際請從環境變數讀取
    $testData = [
        '簡單字串',
        '中英混合: Hello 世界 123',
        '特殊符號: !@#$%^&*()_+-=[]{}|;:,.<>?',
        'JSON: {"user":123,"role":"admin","permissions":["read","write"]}',
        '', // 空字串
        str_repeat('A', 1000), // 長字串
    ];

    echo '<h3>自定義加密解密測試</h3>';
    echo '<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse; font-family:monospace; font-size:12px;">';
    echo '<tr><th>原始明文</th><th>密文 (前40字元)</th><th>解密結果</th><th>耗時(ms)</th><th>狀態</th></tr>';

    foreach ($testData as $plain) {
        $start = microtime(true);
        try {
            $cipher = CryptoHelper::encrypt($plain, $key);
            $decrypted = CryptoHelper::decrypt($cipher, $key);
            $elapsed = round((microtime(true) - $start) * 1000, 2);
            $ok = ($plain === $decrypted) ? '✅' : '❌';
            echo "<tr><td>" . htmlspecialchars(mb_substr($plain, 0, 50)) . "</td>";
            echo "<td>" . htmlspecialchars(substr($cipher, 0, 40)) . "...</td>";
            echo "<td>" . htmlspecialchars(mb_substr($decrypted, 0, 50)) . "</td>";
            echo "<td>$elapsed</td><td>$ok</td></tr>";
        } catch (Exception $e) {
            echo "<tr><td>" . htmlspecialchars(mb_substr($plain, 0, 50)) . "</td><td colspan='4' style='color:red'>" . $e->getMessage() . "</td></tr>";
        }
    }
    echo '</table>';
}
?>

安全性深度分析

✅ 設計優點

  • 語意安全性:隨機 $sessionKey 確保相同明文每次加密結果不同。
  • 完整性檢測:首尾 MD5 可偵測密文截斷/竄改 (但非標準 HMAC)。
  • 二進位安全:逐字節 XOR 不受字符編碼影響,支援 UTF-8 中文、二進位資料。
  • 金鑰派生:MD5 將任意長度金鑰標準化為 128 位。

⚠️ 關鍵弱點 (不可用於高安全場景)

弱點說明風險等級
已知明文攻擊 (KPA)攻擊者若知部分明文 (如固定 JSON 結構),可推導會話金鑰片段,進而破解其他區塊高
選擇明文攻擊 (CPA)攻擊者可任意加密,收集大量密文分析 XOR 模式高
無認證加密 (AEAD)MD5 校驗非加密綁定,理論上存在填充預言機攻擊面中
金鑰重複使用同一金鑰加密大量資料,XOR 流式密碼極易被流密碼分析破解高
隨機數來源rand()/mt_rand() 非密碼學安全 (本碼已改用 random_int)中
側信道攻擊解密過程中字串比較、Base64 解碼耗時可能洩漏資訊低

生產環境替代方案:標準化加密庫

方案一:OpenSSL AES-256-GCM (PHP 7.1+,推薦)

<?php
class StandardCrypto {
    private const CIPHER = 'aes-256-gcm';
    private const KEY_BYTES = 32;
    private const IV_BYTES = 12;
    private const TAG_BYTES = 16;

    public static function encrypt(string $data, string $key): string {
        $key = hash('sha256', $key, true); // 32 bytes
        $iv = random_bytes(self::IV_BYTES);
        $tag = '';
        $ciphertext = openssl_encrypt($data, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv, $tag, '', self::TAG_BYTES);
        if ($ciphertext === false) throw new Exception('Encryption failed');
        // 格式: IV (12) + TAG (16) + CIPHERTEXT
        return base64_encode($iv . $tag . $ciphertext);
    }

    public static function decrypt(string $payload, string $key): string {
        $key = hash('sha256', $key, true);
        $raw = base64_decode($payload, true);
        if (strlen($raw) < self::IV_BYTES + self::TAG_BYTES) throw new Exception('Invalid payload');
        $iv = substr($raw, 0, self::IV_BYTES);
        $tag = substr($raw, self::IV_BYTES, self::TAG_BYTES);
        $ciphertext = substr($raw, self::IV_BYTES + self::TAG_BYTES);
        $plaintext = openssl_decrypt($ciphertext, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv, $tag, '', self::TAG_BYTES);
        if ($plaintext === false) throw new Exception('Decryption failed: wrong key or corrupted data');
        return $plaintext;
    }
}
?>

方案二:Sodium (PHP 7.2+ 內建,最簡單安全)

<?php
class SodiumCrypto {
    public static function encrypt(string $data, string $key): string {
        // 金鑰必須為 SODIUM_CRYPTO_SECRETBOX_KEYBYTES (32) 位元組
        $key = sodium_crypto_secretbox_keygen(); // 實際應從安全配置讀取
        // 或從字串派生: $key = sodium_crypto_generichash($key, '', SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
        $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
        $ciphertext = sodium_crypto_secretbox($data, $nonce, $key);
        return base64_encode($nonce . $ciphertext);
    }

    public static function decrypt(string $payload, string $key): string {
        $key = sodium_crypto_secretbox_keygen(); // 同加密端
        $raw = base64_decode($payload, true);
        $nonce = substr($raw, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
        $ciphertext = substr($raw, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
        $plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
        if ($plaintext === false) throw new Exception('Decryption failed');
        return $plaintext;
    }
}
?>

金鑰管理最佳實踐 (無論採用哪種加密)

  1. 絕不硬編碼:金鑰存放於環境變數 ($_ENV['APP_ENC_KEY'])、配置檔 (權限 600、Git 忽略)、HashiCorp Vault、AWS KMS、Azure Key Vault。
  2. 金鑰輪換:定期 (90-180 天) 更換金鑰,舊資料重新加密或保留舊金鑰解密新資料用新金鑰。
  3. 分級管理:不同敏感度資料使用不同金鑰 (資料庫加密金鑰 ≠ API 傳輸金鑰 ≠ Session 加密金鑰)。
  4. 審計日誌:記錄金鑰存取、加解密操作 (不記錄明文/密文)。

總結

本文提供的 CryptoHelper 類別實現了基於 XOR 流式加密的自定義可逆加密解密,代碼結構清晰、支援中文二進位安全、含完整性校驗。但必須重申:**此為教學/低敏感度用途,非密碼學安全標準**。任何涉及法規遵循 (GDPR、HIPAA、PCI-DSS)、高價值資產、用戶隱私的場景,**強制要求使用 OpenSSL AES-GCM 或 Sodium libsodium 等經標準化驗證的加密庫**,並配合完善的金鑰管理體系。安全無捷徑,選對工具才是正道。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved