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

PHP 加密與 JavaScript 解密實戰:Base64 + XOR + MD5 金鑰衍生方案

     編輯:PHP 相關知識

在前後端分離架構中,有時需在 PHP 端加密敏感資料 (如 Token、用戶 ID、業務參數),前端 JavaScript 解密使用。本文實作一套「Base64 編碼 + XOR 流加密 + MD5 金鑰衍生」的對稱加密方案,並提供完整的 PHP 加密類與對應的 JavaScript 解密代碼 (含純 JS 實現的 MD5 與 Base64)。

加密演算法原理

  1. 明文 → Base64 編碼:將任意二進制/字串轉為可列印字元集,便於傳輸。
  2. 金鑰衍生:固定鹽值 md5('secret_salt') 產生 32 字元十六進位金鑰 (128 bit)。
  3. XOR 流加密:Base64 字串與金鑰循環異或,產生密文位元組序列。
  4. 密文 → Base64 編碼:再次 Base64 編碼,得到最終可安全傳輸的字串。

解密為逆向操作:Base64 解碼 → XOR 還原 → Base64 解碼 → 明文。

PHP 端加密類

<?php
class CryptoBridge {
    private string $secretSalt;

    public function __construct(string $secretSalt = 'your_strong_random_salt_here') {
        $this->secretSalt = $secretSalt;
    }

    /** 獲取 32 字元十六進位金鑰 */
    private function getKey(): string {
        return md5($this->secretSalt);
    }

    /** 加密:回傳 Base64 字串 */
    public function encrypt(string $plaintext): string {
        $b64 = base64_encode($plaintext);
        $key = $this->getKey();
        $keyLen = strlen($key);
        $cipher = '';

        for ($i = 0; $i < strlen($b64); $i++) {
            $k = $i % $keyLen;
            $cipher .= $b64[$i] ^ $key[$k];
        }
        return base64_encode($cipher);
    }

    /** 解密:回傳明文 */
    public function decrypt(string $ciphertext): string {
        $raw = base64_decode($ciphertext, true);
        if ($raw === false) throw new Exception('Invalid Base64 ciphertext');
        $key = $this->getKey();
        $keyLen = strlen($key);
        $plain = '';

        for ($i = 0; $i < strlen($raw); $i++) {
            $k = $i % $keyLen;
            $plain .= $raw[$i] ^ $key[$k];
        }
        $decoded = base64_decode($plain, true);
        if ($decoded === false) throw new Exception('Decryption failed: invalid padding or key');
        return $decoded;
    }
}

// 使用範例
$crypto = new CryptoBridge('my_app_secret_2024');
$encrypted = $crypto->encrypt('user_id=12345&role=admin&expire=1735689600');
echo "Encrypted: $encrypted\n";
// 解密驗證
echo "Decrypted: " . $crypto->decrypt($encrypted) . "\n";
?>

JavaScript 端解密實現

需引入 md5.js 與 base64.js (見下方完整代碼)。

<script src="md5.js"></script>
<script src="base64.js"></script>
<script>
const CRYPTO_SALT = 'my_app_secret_2024'; // 必須與 PHP 端一致

function decrypt(ciphertext) {
    const key = md5(CRYPTO_SALT); // 32 字元 hex 字串
    const raw = Base64.decode(ciphertext); // 解碼為二進制字串
    let plain = '';

    for (let i = 0; i < raw.length; i++) {
        const k = i % key.length;
        plain += String.fromCharCode(raw.charCodeAt(i) ^ key.charCodeAt(k));
    }
    // 二次 Base64 解碼得到明文
    return Base64.decode(plain);
}

// 測試
const encryptedFromPhp = 'U1s1TFN3IQ0reTZbBgJlCA===='; // PHP 加密輸出範例
console.log('Decrypted:', decrypt(encryptedFromPhp)); // 輸出明文
</script>

純 JavaScript MD5 實現 (md5.js)

標準 RFC 1321 實現,無外部依賴,支援 UTF-8 輸入。代碼較長,建議直接下載成熟庫 (如 blueimp-md5),此處僅展示核心結構:

// md5.js - 精簡版結構 (完整代碼請參考 blueimp-md5 或 crypto-js)
var MD5 = (function() {
    // 核心常數與循環左移
    function rotateLeft(n, s) { return (n << s) | (n >>> (32 - s)); }
    // 四輪非線性函數 F, G, H, I
    // ... (省略完整 64 步運算)
    // 輸出格式控制
    var hexcase = 0; // 0=小寫, 1=大寫
    var b64pad = "";
    return {
        hex: function(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); },
        b64: function(s) { return rstr2b64(rstr_md5(str2rstr_utf8(s))); },
        // ... 其他輔助方法
    };
})();

// 使用方式
const hash = MD5.hex('hello world'); // 5eb63bbbe01eeed093cb22bb8f5acdc3

💡 生產環境強烈建議:使用 npm install blueimp-md5 或 crypto-js,或直接用瀏覽器原生 crypto.subtle.digest('SHA-256', ...) (現代瀏覽器支援),避免手寫加密代碼的安全風險。

純 JavaScript Base64 實現 (base64.js)

支援 UTF-8、URL-safe 變體、Node.js Buffer 相容。同樣建議使用成熟庫 (如 base64-js、js-base64):


// base64.js - 結構示意
const Base64 = {
    btoa: (str) => btoa(unescape(encodeURIComponent(str))), // 現代瀏覽器原生支援
    atob: (str) => decodeURIComponent(escape(atob(str))),
    encode: (str, urlSafe = false) => { /* ... */ },
    decode: (str, urlSafe = false) => { /* ... */ }
};

// 現代瀏覽器可直接用
const encoded = btoa('中文測試'); // 5Lit5paH5a2X5rWL6K+V
const decoded = atob(encoded);   // 中文測試

安全性評估與改進建議

層面現狀風險改進
加密強度XOR 流密碼 + 固定金鑰已知明文攻擊可還原金鑰;重放攻擊改用 AES-GCM / ChaCha20-Poly1305 (需引入 Web Crypto API 或 sodium.js)
金鑰管理硬編碼鹽值原始碼洩露即金鑰洩露後端動態下發一次性金鑰 (KMS)、或用 ECDH 協商會話金鑰
完整性驗證無密文竄改無法檢測加入 HMAC-SHA256 或使用認證加密 (AEAD)
重放防護無舊密文可重複使用明文加入 timestamp/nonce,解密後驗證時效
前端安全金鑰暴露於前端代碼任何人可解密此方案僅適用「防誠實用戶竄改」或「混淆非核心參數」,不可用於真正機密資料

現代最佳替代方案

方案一:JWT (JSON Web Token) - 標準化、無狀態、廣泛支援

// PHP (firebase/php-jwt)
use Firebase\JWT\JWT;
$token = JWT::encode(['uid' => 123, 'exp' => time()+3600], $secretKey, 'HS256');

// JS (jsrsasign 或直接驗證簽名)

方案二:Web Crypto API (瀏覽器原生、無依賴)

// 前端生成金鑰對、加密/解密、簽名/驗證
const keyPair = await crypto.subtle.generateKey({name: 'RSA-OAEP', modulusLength: 2048}, true, ['encrypt', 'decrypt']);
const encrypted = await crypto.subtle.encrypt({name: 'RSA-OAEP'}, keyPair.publicKey, plaintextBuffer);

方案三:libsodium (PHP 7.2+ 內建、JS 移植可用)

// PHP
$key = sodium_crypto_secretbox_keygen();
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$cipher = sodium_crypto_secretbox($message, $nonce, $key);
// 發送 nonce + cipher (Base64)
// JS 端用 sodium.js 或 tweetnacl.js 解密

本文提供的 XOR+Base64 方案適合「快速原型、非核心參數混淆、學習加密原理」。任何涉及真實用戶隱私、支付憑證、權限 Token 的場景,**請務必使用經過專業審計的標準加密庫與協議 (JWT, JWE, libsodium, Web Crypto)**,切勿自行拼裝加密邏輯上線。

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