mcrypt 擴充自 PHP 7.2 起棄用,PHP 8.0 移除。 請改用 OpenSSL 或 Sodium (libsodium) 實作加密。本文提供 OpenSSL 版 AES 實作。
<?php
class AesEncryption
{
private string $cipher;
private string $key;
private string $iv;
/**
* 建構子
*
* @param string $key 16/24/32 位元組 (AES-128/192/256)
* @param string $iv 16 位元組 (CBC 模式必須)
* @param int $bits 128, 192, 256
*/
public function __construct(string $key, string $iv, int $bits = 256)
{
// 驗證金鑰長度
$keyLen = strlen($key);
$validKeys = [16 => 128, 24 => 192, 32 => 256];
if (!isset($validKeys[$keyLen])) {
throw new InvalidArgumentException("金鑰長度必須為 16/24/32 位元組,目前:{$keyLen}");
}
if ($validKeys[$keyLen] !== $bits) {
throw new InvalidArgumentException("金鑰長度與位元數不符");
}
// 驗證 IV 長度 (AES 區塊大小固定 16 位元組)
if (strlen($iv) !== 16) {
throw new InvalidArgumentException("IV 長度必須為 16 位元組");
}
$this->cipher = "AES-{$bits}-CBC";
$this->key = $key;
$this->iv = $iv;
}
/**
* 加密 (回傳 Base64 字串)
*/
public function encrypt(string $data): string
{
$encrypted = openssl_encrypt(
$data,
$this->cipher,
$this->key,
OPENSSL_RAW_DATA,
$this->iv
);
if ($encrypted === false) {
throw new RuntimeException('加密失敗: ' . openssl_error_string());
}
return base64_encode($encrypted);
}
/**
* 解密 (輸入 Base64 字串)
*/
public function decrypt(string $encryptedData): string
{
$decoded = base64_decode($encryptedData, true);
if ($decoded === false) {
throw new InvalidArgumentException('無效的 Base64 資料');
}
$decrypted = openssl_decrypt(
$decoded,
$this->cipher,
$this->key,
OPENSSL_RAW_DATA,
$this->iv
);
if ($decrypted === false) {
throw new RuntimeException('解密失敗: ' . openssl_error_string());
}
return $decrypted;
}
}
// 金鑰與 IV 應從安全配置/環境變數取得,勿硬編碼
$key = random_bytes(32); // AES-256 需 32 位元組
$iv = random_bytes(16); // 每次加密應使用隨機 IV
$aes = new AesEncryption($key, $iv, 256);
$plaintext = '機密資料:API 金鑰、個資等';
$encrypted = $aes->encrypt($plaintext);
$decrypted = $aes->decrypt($encrypted);
echo "原文:{$plaintext}\n";
echo "加密:{$encrypted}\n";
echo "解密:{$decrypted}\n";
// 驗證
assert($plaintext === $decrypted);
| 原則 | 說明 |
|---|---|
| 金鑰管理 | 使用 KMS/環境變數/密鑰保管庫,勿寫死程式碼 |
| IV 隨機性 | 每次加密生成新 IV (random_bytes(16)),隨密文儲存 |
| 模式選擇 | CBC 需正確填充,GCM 提供認證加密 (AEAD) 更安全 |
| 完整性驗證 | CBC 模式建議額外 HMAC 驗證,或改用 GCM |
| 金鑰輪換 | 定期更換金鑰,保留舊金鑰解密歷史資料 |
class AesGcmEncryption
{
public function encrypt(string $data, string $key): array
{
$iv = random_bytes(12); // GCM 建議 12 位元組 IV
$tag = '';
$encrypted = openssl_encrypt(
$data, 'AES-256-GCM', $key,
OPENSSL_RAW_DATA, $iv, $tag, '', 16
);
return [
'data' => base64_encode($encrypted),
'iv' => base64_encode($iv),
'tag' => base64_encode($tag),
];
}
public function decrypt(array $payload, string $key): string
{
return openssl_decrypt(
base64_decode($payload['data']), 'AES-256-GCM', $key,
OPENSSL_RAW_DATA, base64_decode($payload['iv']),
base64_decode($payload['tag']), '', 16
);
}
}
現代替代方案:PHP 7.2+ 內建 Sodium (libsodium),提供 `sodium_crypto_aead_xchacha20poly1305_ietf_encrypt()` 等高層 API,更安全、更簡單、抗旁路攻擊。