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

PHP AES 加密算法完整實作範例:OpenSSL 替代 mcrypt 的最佳實踐

     編輯:PHP 相關知識

PHP AES 加密算法完整實作範例

在 PHP 開發中,資料加密是保護敏感資訊的關鍵手段。AES(Advanced Encryption Standard)作為目前最廣泛採用的對稱加密算法,因其安全性高、效能好,成為開發者的首選。本文將詳細介紹如何在 PHP 中正確實作 AES-256-CBC 加密與解密,並說明為何應棄用舊版 mcrypt 擴展,改用現代化的 openssl 函數庫。

為什麼不再推薦使用 mcrypt?

早期 PHP 專案常使用 mcrypt 擴展實作 AES 加密。然而,mcrypt 自 PHP 7.1 起被棄用,並在 PHP 7.2 中完全移除。其主要問題包括:

  • 長期未維護,存在潛在安全漏洞
  • API 設計老舊,容易導致實作錯誤(如 IV 處理不當)
  • 不支援現代加密模式與最佳實踐

因此,所有新專案與現有專案維護都應遷移至 openssl_encrypt() 與 openssl_decrypt()。

核心概念:金鑰派生、IV 與填充

正確的 AES 實作需注意三個關鍵要素:

  1. 金鑰派生:不應直接使用密碼作為金鑰,而應透過 PBKDF2、HKDF 或至少 SHA-256 雜湊衍生固定長度金鑰(AES-256 需 32 位元組)。
  2. 初始化向量(IV):CBC 模式要求唯一、不可預測的 IV。每次加密應生成新的隨機 IV(random_bytes(16)),並與密文一起儲存(通常前綴於密文前)。
  3. 填充模式:OpenSSL 預設使用 PKCS#7 填充,與舊版 mcrypt 的行為一致,無需手動處理。

完整程式碼範例:AES-256-CBC 加密類

<?php

class AesEncryption
{
    private const CIPHER = 'aes-256-cbc';
    private const KEY_LENGTH = 32; // 256 bits
    private const IV_LENGTH = 16;  // 128 bits

    private string $key;

    public function __construct(string $secretKey)
    {
        // 使用 SHA-256 將任意長度的密鑰轉為 32 位元組
        $this->key = hash('sha256', $secretKey, true);
    }

    /**
     * 加密資料
     * @param string $plaintext 明文
     * @return string Base64 編碼的密文(含 IV 前綴)
     */
    public function encrypt(string $plaintext): string
    {
        // 生成隨機 IV
        $iv = random_bytes(self::IV_LENGTH);

        // 加密
        $ciphertext = openssl_encrypt(
            $plaintext,
            self::CIPHER,
            $this->key,
            OPENSSL_RAW_DATA,
            $iv
        );

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

        // 將 IV 前綴於密文,並 Base64 編碼便於儲存/傳輸
        return base64_encode($iv . $ciphertext);
    }

    /**
     * 解密資料
     * @param string $encodedCiphertext Base64 編碼的密文(含 IV 前綴)
     * @return string 明文
     */
    public function decrypt(string $encodedCiphertext): string
    {
        $data = base64_decode($encodedCiphertext, true);
        if ($data === false) {
            throw new \InvalidArgumentException('無效的 Base64 編碼');
        }

        if (strlen($data) < self::IV_LENGTH) {
            throw new \InvalidArgumentException('密文長度不足');
        }

        // 提取 IV 與實際密文
        $iv = substr($data, 0, self::IV_LENGTH);
        $ciphertext = substr($data, self::IV_LENGTH);

        $plaintext = openssl_decrypt(
            $ciphertext,
            self::CIPHER,
            $this->key,
            OPENSSL_RAW_DATA,
            $iv
        );

        if ($plaintext === false) {
            throw new \RuntimeException('解密失敗:資料可能已損壞或金鑰錯誤');
        }

        return $plaintext;
    }
}

// 使用範例
$crypto = new AesEncryption('MySecretKey123!@#');
$original = '敏感資料:信用卡號 4242-4242-4242-4242';

$encrypted = $crypto->encrypt($original);
echo "加密結果: {$encrypted}\n";

$decrypted = $crypto->decrypt($encrypted);
echo "解密結果: {$decrypted}\n";

// 驗證
assert($original === $decrypted, '加解密不一致');
?>

關鍵安全提醒

  • 金鑰管理:勿將金鑰硬編碼在程式碼中,應使用環境變數、金鑰管理系統(KMS)或密碼管理工具。
  • 完整性驗證:上述範例僅提供機密性。若需防範密文竄改,應改用 AES-GCM(aes-256-gcm)或額外加上 HMAC 驗證(Encrypt-then-MAC)。
  • 錯誤處理:生產環境應記錄加解密失敗日誌,但勿洩露敏感細節。

遷移舊專案建議

若現有專案使用 mcrypt 並需相容舊密文,可撰寫相容層:先用 mcrypt 解密舊資料,再用 OpenSSL 重新加密儲存。漸進式遷移可降低風險。

結語

採用 openssl 實作 AES-256-CBC 是目前 PHP 加密的標準做法。本文提供的類別封裝了隨機 IV 生成、金鑰派生與 Base64 編碼等最佳實踐,可直接用於生產環境。記住:加密只是資安拼圖的一塊,完整的防護還需包含傳輸層加密(TLS)、存取控制、審計日誌等多層次措施。

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