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

PHP Cookie 加密實作指南:使用 Rijndael-256 保護敏感資料

     編輯:PHP 綜合知識

在 Web 開發中,Cookie 常被用來儲存用戶會話、偏好設定或追蹤資訊。然而,預設的 Cookie 以明文傳輸與儲存,極易遭竄改或竊取。本文將示範如何以 PHP 實作一個可重複使用的 Cookie 加密類別,採用 Rijndael-256 (AES) 演算法搭配 ECB 模式,並透過 Base64 編碼確保資料在 HTTP 標頭中安全傳遞。

核心設計概念

加密流程分為三個步驟:

  1. 從設定檔讀取金鑰(Config::get('secret_key'))
  2. 使用 mcrypt_encrypt 以 Rijndael-256 / ECB 模式加密明文
  3. 對加密後的二進位資料進行 base64_encode,產生可安全存入 Cookie 的字串

解密則為逆向操作:Base64 解碼 → mcrypt 解密 → 去除填充字元。

完整程式碼實作

<?php
class Cookie
{
    /**
     * 解密已加密的 Cookie 值
     * @param string $encryptedText Base64 編碼的密文
     * @return string 解密後的明文
     */
    private static function _decrypt($encryptedText)
    {
        $key = Config::get('secret_key');
        $cryptText = base64_decode($encryptedText);
        $ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
        $iv = mcrypt_create_iv($ivSize, MCRYPT_RAND);
        $decryptText = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $cryptText, MCRYPT_MODE_ECB, $iv);
        return trim($decryptText);
    }

    /**
     * 加密 Cookie 值
     * @param string $plainText 待加密的明文
     * @return string Base64 編碼的密文
     */
    private static function _encrypt($plainText)
    {
        $key = Config::get('secret_key');
        $ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
        $iv = mcrypt_create_iv($ivSize, MCRYPT_RAND);
        $encryptText = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $plainText, MCRYPT_MODE_ECB, $iv);
        return trim(base64_encode($encryptText));
    }

    /**
     * 刪除指定 Cookie
     * @param array $args ['name' => string, 'domain' => string?]
     * @return bool
     */
    public static function del($args)
    {
        $name = $args['name'];
        $domain = isset($args['domain']) ? $args['domain'] : null;
        return isset($_COOKIE[$name]) ? setcookie($name, '', time() - 86400, '/', $domain) : true;
    }

    /**
     * 取得並解密 Cookie 值
     * @param string $name Cookie 名稱
     * @return string|null 解密後的值,不存在則回傳 null
     */
    public static function get($name)
    {
        return isset($_COOKIE[$name]) ? self::_decrypt($_COOKIE[$name]) : null;
    }

    /**
     * 設定加密 Cookie
     * @param array $args ['name','value','expire?','path?','domain?','secure?']
     * @return bool
     */
    public static function set($args)
    {
        $name = $args['name'];
        $value = self::_encrypt($args['value']);
        $expire = isset($args['expire']) ? $args['expire'] : null;
        $path = isset($args['path']) ? $args['path'] : '/';
        $domain = isset($args['domain']) ? $args['domain'] : null;
        $secure = isset($args['secure']) ? $args['secure'] : 0;
        return setcookie($name, $value, $expire, $path, $domain, $secure);
    }
}
?>

使用範例

<?php
// 寫入加密 Cookie(有效期 1 天)
Cookie::set([
    'name'  => 'user_token',
    'value' => 'user_12345|role_admin',
    'expire' => time() + 86400,
    'secure' => true,          // 僅 HTTPS 傳輸
    'path'   => '/',
    'domain' => '.example.com'
]);

// 讀取並自動解密
$token = Cookie::get('user_token');
if ($token) {
    list($uid, $role) = explode('|', $token);
    echo "歡迎回來,使用者 $uid ($role)";
}

// 登出時刪除
Cookie::del(['name' => 'user_token', 'domain' => '.example.com']);
?>

安全性補充說明

  • 金鑰管理:secret_key 應為 32 位元組(256-bit)高熵亂數,切勿硬編碼於程式碼,建議存放於環境變數或獨立設定檔(.env)。
  • ECB 模式限制:ECB 不使用初始化向量(IV),相同明文會產生相同密文,若資料結構可預測可能洩漏模式。生產環境建議改用 CBC 或 GCM 模式,並為每次加密產生隨機 IV,將 IV 與密文一併儲存。
  • 棄用 mcrypt:PHP 7.2 起 mcrypt 已移除,現代專案請改用 openssl_encrypt / openssl_decrypt 搭配 AES-256-CBC 或 AES-256-GCM。
  • 完整性驗證:單純加密無法防止密文被竄改,建議額外加上 HMAC-SHA256 或採用認證加密(AEAD)模式(如 GCM)。

現代化重構範例(PHP 7.2+)

<?php
class SecureCookie
{
    private const CIPHER = 'AES-256-GCM';
    private const KEY_LEN = 32;   // 256-bit
    private const IV_LEN = 12;    // GCM 建議 96-bit
    private const TAG_LEN = 16;

    public static function set(string $name, string $value, int $expire = 0, string $path = '/', ?string $domain = null, bool $secure = true, bool $httponly = true): bool
    {
        $key = Config::get('secret_key'); // 必須為 32 bytes
        $iv = random_bytes(self::IV_LEN);
        $ciphertext = openssl_encrypt($value, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv, $tag);
        $payload = base64_encode($iv . $tag . $ciphertext);
        return setcookie($name, $payload, $expire, $path, $domain, $secure, $httponly);
    }

    public static function get(string $name): ?string
    {
        if (!isset($_COOKIE[$name])) return null;
        $key = Config::get('secret_key');
        $raw = base64_decode($_COOKIE[$name], true);
        if ($raw === false) return null;
        $iv = substr($raw, 0, self::IV_LEN);
        $tag = substr($raw, self::IV_LEN, self::TAG_LEN);
        $ciphertext = substr($raw, self::IV_LEN + self::TAG_LEN);
        $plaintext = openssl_decrypt($ciphertext, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv, $tag);
        return $plaintext !== false ? $plaintext : null;
    }
}
?>

採用 GCM 模式可同時提供機密性與完整性,且 openssl 為 PHP 內建擴展,無需額外安裝,是現代專案的推薦選擇。

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