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

PHP 對稱加密實戰:基於 XOR 與 Base64 的輕量級加解密函數解析與安全性評估

     編輯:PHP 相關知識

代碼還原與運行機制分析

<?php
/**
 * 加密:明文 + 隨機密鑰流 XOR → Base64
 * @param string $str 明文
 * @param string $key 主密鑰
 * @return string 密文
 */
function passport_encrypt(string $str, string $key): string {
    // 1. 生成一次性隨機密鑰流(32 字符 MD5)
    srand((double)microtime() * 1000000); // 老版本播種,PHP 4.2+ 自動播種可省略
    $encrypt_key = md5(rand(0, 32000));    // 32 字符十六進制字符串

    // 2. 明文與密鑰流逐字符 XOR,並插入密鑰流字符作為標記
    $ctr = 0;
    $tmp = '';
    for ($i = 0; $i < strlen($str); $i++) {
        $ctr = $ctr === strlen($encrypt_key) ? 0 : $ctr; // 循環使用密鑰流
        $tmp .= $encrypt_key[$ctr] . ($str[$i] ^ $encrypt_key[$ctr++]); // 插入密鑰字符 + XOR結果
    }
    // $tmp 長度 = 2 * strlen($str),格式:k1⊕c1 k2⊕c2 ...

    // 3. 二次 XOR:用主密鑰 MD5 加密上述中間文本
    return base64_encode(passport_key($tmp, $key));
}

/**
 * 解密:Base64 解碼 → 主密鑰 XOR 還原中間文本 → 提取密鑰流字符 → XOR 還原明文
 */
function passport_decrypt(string $str, string $key): string {
    $str = passport_key(base64_decode($str), $key); // 還原中間文本
    $tmp = '';
    for ($i = 0; $i < strlen($str); $i++) {
        $md5 = $str[$i];        // 密鑰流字符
        $tmp .= $str[++$i] ^ $md5; // 取下一字符 XOR 還原
    }
    return $tmp;
}

/**
 * 核心 XOR 函數:數據與密鑰流循環 XOR
 */
function passport_key(string $str, string $encrypt_key): string {
    $encrypt_key = md5($encrypt_key); // 主密鑰衍生 32 字符流
    $ctr = 0;
    $tmp = '';
    for ($i = 0; $i < strlen($str); $i++) {
        $ctr = $ctr === strlen($encrypt_key) ? 0 : $ctr;
        $tmp .= $str[$i] ^ $encrypt_key[$ctr++];
    }
    return $tmp;
}

// 測試
$str = '作者:WWW.JB51.NET';
$key = 'jb51net';
$encrypt = passport_encrypt($str, $key);
$decrypt = passport_decrypt($encrypt, $key);
echo "原文:$str\n密文:$encrypt\n譯文:$decrypt\n";

算法結構圖解

加密流程:
明文 "ABC" (3字節)
    │
    ├─ 生成隨機密鑰流 K = md5(rand()) = "k1k2k3...k32" (32字節)
    │
    ├─ 第一輪 XOR + 插入密鑰字符:
    │   i=0: tmp += K[0] . (A ^ K[0])  → "k1⊕A"
    │   i=1: tmp += K[1] . (B ^ K[1])  → "k2⊕B"
    │   i=2: tmp += K[2] . (C ^ K[2])  → "k3⊕C"
    │   中間文本 M = "k1⊕A k2⊕B k3⊕C" (6字節)
    │
    ├─ 第二輪 XOR:主密鑰 key → md5(key) = "m1m2...m32"
    │   M 與 "m1m2..." 循環 XOR → C'
    │
    └─ Base64 編碼 → 最終密文

解密流程:
密文 → Base64 解碼 → 與 md5(key) XOR 還原 M → 逐對讀取 (K[i], M[i]) → 明文 = K[i] ^ M[i]

安全性深度評估:嚴重缺陷,禁止生產使用

缺陷影響攻擊示例
隨機數源弱rand() 非加密安全,種子空間小(microtime()*1e6 可預測),md5(rand()) 熵極低重放攻擊、密鑰流預測、已知明文攻擊恢復密鑰流
自定義 XOR 模式插入密鑰字符導致密文長度翻倍,洩露明文長度;密鑰流循環使用,違反一次性密碼本原則頻率分析、已知明文攻擊輕松破解
無完整性驗證任意修改密文解密後產生可控明文變化,無 MAC/認證標籤填充 oracle 攻擊、密文篡改(如修改金額、權限位)
密鑰衍生弱直接 md5(key),無鹽、無迭代、無記憶體硬度,GPU 可秒級暴力破解弱密碼離線字典攻擊、彩虹表
ECB 模式等價塊大小實際為 1 字節,相同明文字節產生相同密文字節(受隨機密鑰流影響部分緩解,但主密鑰 XOR 為固定流)模式識別、重放
Base64 非加密僅編碼,無安全屬性直接可見密文結構

現代替代方案:libsodium / OpenSSL 高級 API

方案一:Sodium (PHP 7.2+ 內建) —— 首選

// 對稱加密:XChaCha20-Poly1305 (AEAD,帶認證加密)
function encrypt(string $plaintext, string $key): string {
    // $key 必須為 SODIUM_CRYPTO_SECRETBOX_KEYBYTES (32字節) 隨機生成
    $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); // 24字節
    $ciphertext = sodium_crypto_secretbox($plaintext, $nonce, $key);
    // 存儲:nonce + ciphertext (Base64 或二進制)
    return base64_encode($nonce . $ciphertext);
}

function decrypt(string $encoded, string $key): string|false {
    $data = base64_decode($encoded);
    $nonce = substr($data, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
    $ciphertext = substr($data, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
    return sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
}

// 密鑰生成與存儲
$key = sodium_crypto_secretbox_keygen(); // 32字節隨機
// 安全存儲:環境變量、密鑰管理系統、硬件安全模塊 (HSM)
// 切勿硬編碼在代碼中!

方案二:OpenSSL AES-256-GCM (PHP 7.1+)

function openssl_encrypt_aead(string $plaintext, string $key, string $aad = ''): string {
    $ivLen = 12; // GCM 建議 96 位 IV
    $iv = random_bytes($ivLen);
    $tag = '';
    $ciphertext = openssl_encrypt(
        $plaintext,
        'aes-256-gcm',
        $key,
        OPENSSL_RAW_DATA,
        $iv,
        $tag,
        $aad,      // 附加認證數據
        16         // 標籤長度
    );
    if ($ciphertext === false) throw new Exception('Encryption failed');
    return base64_encode($iv . $tag . $ciphertext);
}

function openssl_decrypt_aead(string $encoded, string $key, string $aad = ''): string|false {
    $data = base64_decode($encoded);
    $ivLen = 12;
    $tagLen = 16;
    $iv = substr($data, 0, $ivLen);
    $tag = substr($data, $ivLen, $tagLen);
    $ciphertext = substr($data, $ivLen + $tagLen);
    return openssl_decrypt(
        $ciphertext,
        'aes-256-gcm',
        $key,
        OPENSSL_RAW_DATA,
        $iv,
        $tag,
        $aad
    );
}

密鑰管理最佳實踐

  1. 環境隔離:開發/測試/預發/生產使用完全不同的主密鑰。
  2. 輪換機制:定期(如 90 天)輪換主密鑰,舊數據重新加密或保留舊密鑰解密標識。
  3. 最小權限:應用進程僅讀密鑰,寫入權限僅限部署管道/密鑰管理系統。
  4. 審計日誌:記錄密鑰訪問、加解密操作(不記錄明文/密文)。

何時可以用原文代碼?

  • ✅ 學習 XOR、Base64、流密碼基本原理
  • ✅ CTF 逆向題、混淆非敏感配置(如前端 JS 混淆)
  • ✅ 極受限環境(無 OpenSSL/Sodium、無法安裝擴展)的**非安全**數據輕度混淆
  • ❌ 任何涉及用戶隱私、支付憑證、Token、API 密鑰、業務核心數據的場景

加密是專業領域,**切勿自行設計加密算法**。PHP 現代版本內建 sodium(基於 libsodium,經過密碼學專家審計、標準化、硬件加速),配合標準化的密鑰管理流程,才是保護數據機密性、完整性、真實性的正確道路。將原文代碼作為教學案例研究其缺陷,而非複製粘貼進項目。

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