在需要「可解密」的場景(如 API Token、配置文件敏感信息、第三方憑證存儲)中,單向哈希(MD5/SHA)不適用。本文提供一個開箱即用的對稱加密類,採用 AES-256 (Rijndael-256) ECB 模式,並處理 Base64 URL 安全編碼。
<?php
class Crypt {
private $skey; // 32 字節二進制密鑰
/**
* 構造函數:從任意長度字符串派生 256 位密鑰
* @param string $key 主密鑰(建議 32+ 字符高熵值字符串)
*/
public function __construct($key) {
// 使用 MD5 哈希將任意長度密鑰固化為 32 字節(256 位)
// true 返回原始二進制格式而非十六進制字符串
$this->skey = hash("md5", $key, true);
}
/**
* Base64URL 安全編碼:替換 + / = 為 - _ 空
* 適用於 URL 參數、Cookie、Header 等場景
*/
public function safe_b64encode($string) {
$data = base64_encode($string);
$data = str_replace(array('+', '/', '='), array('-', '_', ''), $data);
return $data;
}
/**
* Base64URL 安全解碼:還原 - _ 為 + / 並補齊 =
*/
public function safe_b64decode($string) {
$data = str_replace(array('-', '_'), array('+', '/'), $string);
$mod4 = strlen($data) % 4;
if ($mod4) {
$data .= substr('====', $mod4); // 補齊 padding
}
return base64_decode($data);
}
/**
* 加密
* @param string $value 明文
* @return string|false 密文(Base64URL)或失敗 false
*/
public function encode($value) {
if (!$value) return false;
$text = $value;
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); // ECB 模式 IV 不使用但必須提供
$crypttext = mcrypt_encrypt(
MCRYPT_RIJNDAEL_256,
$this->skey,
$text,
MCRYPT_MODE_ECB,
$iv
);
return trim($this->safe_b64encode($crypttext));
}
/**
* 解密
* @param string $value 密文(Base64URL)
* @return string|false 明文或失敗 false
*/
public function decode($value) {
if (!$value) return false;
$crypttext = $this->safe_b64decode($value);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypttext = mcrypt_decrypt(
MCRYPT_RIJNDAEL_256,
$this->skey,
$crypttext,
MCRYPT_MODE_ECB,
$iv
);
return trim($decrypttext);
}
}
?>
<?php
require_once 'Crypt.php';
// 初始化(生產環境從配置文件/環境變量讀取)
$crypt = new Crypt('your-super-secret-master-key-2024!@#');
// 加密
$plaintext = '{"user_id":12345,"role":"admin","exp":1735689600}';
$ciphertext = $crypt->encode($plaintext);
echo "加密結果: $ciphertext\n";
// 輸出類似: a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6
// 解密
$decrypted = $crypt->decode($ciphertext);
echo "解密結果: $decrypted\n";
// 輸出: {"user_id":12345,"role":"admin","exp":1735689600}
// 驗證
var_dump($plaintext === $decrypted); // bool(true)
?>
| 組件 | 選型 | 說明 |
|---|---|---|
| 加密算法 | Rijndael-256 (AES-256) | 區塊大小 256 位,密鑰 256 位,安全性高 |
| 加密模式 | ECB (Electronic Codebook) | ⚠️ 不安全:相同明文塊產生相同密文塊,無 IV 隨機性 |
| 密鑰派生 | MD5($master_key) | 固定 32 字節,無 Salt、無迭代,抗暴力破解弱 |
| IV 生成 | mcrypt_create_iv(RAND) | ECB 模式忽略 IV,此處僅滿足 API 要求 |
| 編碼輸出 | Base64URL (RFC 4648 §5) | URL 安全,無 + / =,適合 Token 傳輸 |
| 填充方式 | mcrypt 默認 Zero Padding | 解密後 trim() 移除,若明文含尾部空格會丟失 |
<?php
class ModernCrypt {
private $key; // 32 字節原始密鑰
public function __construct($master_key, $salt = '') {
// 使用 PBKDF2 派生密鑰(含 Salt、高迭代次數)
$this->key = hash_pbkdf2('sha256', $master_key, $salt ?: random_bytes(16), 100000, 32, true);
}
public function encrypt($plaintext, $associated_data = '') {
$iv = random_bytes(12); // GCM 建議 96 位 IV
$ciphertext = openssl_encrypt(
$plaintext,
'aes-256-gcm', // AES-256 GCM 模式(認證加密)
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag, // 認證標籤
$associated_data // AAD:關聯數據(如用戶 ID、時間戳)
);
// 組合:IV (12) + Tag (16) + Ciphertext
return $this->safe_b64encode($iv . $tag . $ciphertext);
}
public function decrypt($ciphertext_b64, $associated_data = '') {
$data = $this->safe_b64decode($ciphertext_b64);
if (strlen($data) < 28) return false; // IV(12)+Tag(16) 最小長度
$iv = substr($data, 0, 12);
$tag = substr($data, 12, 16);
$ciphertext = substr($data, 28);
return openssl_decrypt(
$ciphertext,
'aes-256-gcm',
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag,
$associated_data
);
}
// 同前文 safe_b64encode/decode 方法...
}
?>
加密是系統安全的基石,選擇正確的庫、模式、密鑰管理方式,比實現算法本身更關鍵。