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

PHP 自定義加密解密實戰:從簡單替換到 AES 標準算法

     編輯:PHP 相關知識

加密是數據安全的基石。本文遞進式介紹三種 PHP 加密方案:字符映射替換(教學用)、自定義流加密(不推薦生產)、標準 AES-GCM(生產首選),並強調密鑰管理、IV 隨機性、完整性驗證等核心安全原則。

方案一:字符映射替換(僅供理解原理)

<?php
/**
 * 簡單字符替換加密(極不安全,僅作演示)
 */
class SimpleSubstitutionCipher
{
    private string $alphabet = 'abA!c1dB#ef2@Cg$h%iD_3jkl^E:m}4n.o{&F*p)5q(G-r[sH]6tuIv7w+Jxy8z9K0';
    private string $cipher   = 'zAy%0Bx+1C$wDv^Eu2-t3(F{sr&G4q_pH5*on6I)m:l7.Jk]j8K}ih@gf9#ed!cb[';

    public function encrypt(string $str): string
    {
        $map = array_combine(str_split($this->alphabet), str_split($this->cipher));
        return strtr($str, $map);
    }

    public function decrypt(string $str): string
    {
        $map = array_combine(str_split($this->cipher), str_split($this->alphabet));
        return strtr($str, $map);
    }
}

// 使用
$cipher = new SimpleSubstitutionCipher();
$enc = $cipher->encrypt('www.example.com');
$dec = $cipher->decrypt($enc);
echo "加密: $enc\n解密: $dec\n";
?>

缺陷:單表替換密碼,頻率分析即可破解;無完整性校驗;密鑰硬編碼;不支持二進制數據。

方案二:原文中的自定義流加密(改進版,仍不推薦生產)

<?php
/**
 * 基於 MD5 衍生密鑰流的 XOR 加密(教學展示,生產請用 AES)
 */
class LegacyStreamCipher
{
    public function encrypt(string $plaintext, string $key): string
    {
        $encryptKey = md5(mt_rand(0, 32000)); // 每次加密生成隨機密鑰流種子
        $ctr = 0;
        $ciphertext = '';
        for ($i = 0; $i < strlen($plaintext); $i++) {
            $ctr = $ctr === strlen($encryptKey) ? 0 : $ctr;
            $ciphertext .= $encryptKey[$ctr] . ($plaintext[$i] ^ $encryptKey[$ctr++]);
        }
        // 二次加密:使用用戶密鑰衍生的密鑰流
        return base64_encode($this->xorCipher($ciphertext, md5($key)));
    }

    public function decrypt(string $ciphertextB64, string $key): string
    {
        $ciphertext = base64_decode($ciphertextB64);
        $ciphertext = $this->xorCipher($ciphertext, md5($key));
        $plaintext = '';
        for ($i = 0; $i < strlen($ciphertext); $i++) {
            $k = $ciphertext[$i];
            $plaintext .= $ciphertext[++$i] ^ $k;
        }
        return $plaintext;
    }

    private function xorCipher(string $data, string $key): string
    {
        $ctr = 0;
        $out = '';
        for ($i = 0; $i < strlen($data); $i++) {
            $ctr = $ctr === strlen($key) ? 0 : $ctr;
            $out .= $data[$i] ^ $key[$ctr++];
        }
        return $out;
    }
}
?>

風險:MD5 已被碰撞攻擊破解;自定義密鑰衍生非標準;無認證加密(AEAD),密文可被篡改;隨機數來源 mt_rand 非加密安全。

方案三:生產級標準 —— AES-256-GCM(推薦)

PHP 7.1+ 通过 OpenSSL 擴展原生支持 AES-GCM(認證加密),同時提供機密性、完整性、真實性。

<?php
/**
 * 現代化 AES-256-GCM 加密工具類
 * 需 PHP 7.1+ 與 OpenSSL 擴展
 */
class AesGcmCipher
{
    private const CIPHER = 'aes-256-gcm';
    private const KEY_LENGTH = 32;   // 256 bits
    private const IV_LENGTH = 12;    // 96 bits (GCM 推薦)
    private const TAG_LENGTH = 16;   // 128 bits

    /**
     * 加密
     * @param string $plaintext 明文
     * @param string $keyBase64 Base64 編碼的 32 字節主密鑰
     * @param string|null $aad 附加認證數據(可選,如用戶ID、時間戳)
     * @return string Base64 編碼的密文 (IV + Ciphertext + Tag)
     */
    public static function encrypt(string $plaintext, string $keyBase64, ?string $aad = null): string
    {
        $key = base64_decode($keyBase64);
        if (strlen($key) !== self::KEY_LENGTH) {
            throw new \InvalidArgumentException('密鑰必須為 32 字節 (Base64 編碼後 44 字符)');
        }

        $iv = random_bytes(self::IV_LENGTH);
        $tag = null;

        $ciphertext = openssl_encrypt(
            $plaintext,
            self::CIPHER,
            $key,
            OPENSSL_RAW_DATA,
            $iv,
            $tag,
            $aad ?? ''
        );

        if ($ciphertext === false) {
            throw new \RuntimeException('加密失敗: ' . openssl_error_string());
        }

        // 組合:IV (12) + Ciphertext + Tag (16)
        return base64_encode($iv . $ciphertext . $tag);
    }

    /**
     * 解密
     * @param string $payloadBase64 Base64 編碼的密文 (IV + Ciphertext + Tag)
     * @param string $keyBase64 Base64 編碼的 32 字節主密鑰
     * @param string|null $aad 附加認證數據(需與加密時一致)
     * @return string 明文
     */
    public static function decrypt(string $payloadBase64, string $keyBase64, ?string $aad = null): string
    {
        $key = base64_decode($keyBase64);
        $payload = base64_decode($payloadBase64);

        $iv = substr($payload, 0, self::IV_LENGTH);
        $tag = substr($payload, -self::TAG_LENGTH);
        $ciphertext = substr($payload, self::IV_LENGTH, -self::TAG_LENGTH);

        $plaintext = openssl_decrypt(
            $ciphertext,
            self::CIPHER,
            $key,
            OPENSSL_RAW_DATA,
            $iv,
            $tag,
            $aad ?? ''
        );

        if ($plaintext === false) {
            throw new \RuntimeException('解密失敗(密文被篡改或密鑰錯誤)');
        }

        return $plaintext;
    }

    // 生成隨機主密鑰(僅需執行一次,安全存儲)
    public static function generateKey(): string
    {
        return base64_encode(random_bytes(self::KEY_LENGTH));
    }
}

// === 使用示例 ===
// 1. 生成密鑰(僅執行一次,存入環境變量或密鑰管理系統)
// $masterKey = AesGcmCipher::generateKey(); // 例如: "x7K9mP2qR5vW8zA3..."
$masterKey = 'x7K9mP2qR5vW8zA3...'; // 從配置讀取

// 2. 加密
$plaintext = '敏感數據:用戶身份證號 110101199001011234';
$encrypted = AesGcmCipher::encrypt($plaintext, $masterKey, 'user_12345');
echo "加密後: $encrypted\n";

// 3. 解密
$decrypted = AesGcmCipher::decrypt($encrypted, $masterKey, 'user_12345');
echo "解密後: $decrypted\n";
?>

安全核心原則總結

  1. 不要自行設計加密算法:使用經過學術界驗證的標準(AES、ChaCha20-Poly1305)。
  2. 使用認證加密 (AEAD):AES-GCM、ChaCha20-Poly1305 同時保證機密性與完整性,防止密文篡改攻擊。
  3. 密鑰管理分離:主密鑰存儲於環境變量/密鑰管理系統(HashiCorp Vault、AWS KMS),代碼中僅讀取,絕不硬編碼。
  4. IV/Nonce 唯一性:每次加密使用 random_bytes() 生成隨機 IV,同一密鑰下 IV 絕不重複。
  5. 附加認證數據 (AAD):綁定業務上下文(用戶 ID、請求 ID、時間戳),防止密文在不同上下文間重放。
  6. 棄用 MCrypt:PHP 7.2 移除,OpenSSL 是唯一標準擴展。
加密只是安全拼圖的一塊。傳輸層需 TLS 1.3、存儲層需磁盤加密、應用層需最小權限原則、審計日誌與入侵檢測體系才能構建纵深防御。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved