提供一個簡單、可復用的 PHP 加密解密類,滿足以下需求:
<?php
/**
* SysCrypt - 對稱加密工具類
* 算法特點:基於隨機密鑰流的 XOR 加密 + Base64 編碼 + 主鑰二次混淆
* 適用場景:中短文本加密、配置保護、參數簽名
* 不適用場景:大文件加密、高安全等級需求(請使用 OpenSSL AES-256-GCM)
*/
class SysCrypt {
private string $cryptKey;
public function __construct(string $cryptKey) {
if (empty($cryptKey)) {
throw new InvalidArgumentException('加密主鑰不能為空');
}
$this->cryptKey = $cryptKey;
}
/**
* 加密文本
* @param string $plaintext 明文
* @return string Base64 編碼的密文
*/
public function encrypt(string $plaintext): string {
// 1. 生成隨機會話鑰 (16字節足夠)
$sessionKey = bin2hex(random_bytes(16)); // 32字符十六進制
// 2. 明文與會話鑰 XOR 混淆 (交織模式)
$obfuscated = '';
$keyLen = strlen($sessionKey);
for ($i = 0; $i < strlen($plaintext); $i++) {
$obfuscated .= $sessionKey[$i % $keyLen] . ($plaintext[$i] ^ $sessionKey[$i % $keyLen]);
}
// 3. 主鑰二次加密
$encrypted = $this->xorWithKey($obfuscated, $this->cryptKey);
// 4. Base64 編碼便於傳輸存儲
return base64_encode($encrypted);
}
/**
* 解密文本
* @param string $ciphertext Base64 編碼密文
* @return string 明文
* @throws Exception 解密失敗
*/
public function decrypt(string $ciphertext): string {
// 1. Base64 解碼
$decoded = base64_decode($ciphertext, true);
if ($decoded === false) {
throw new Exception('無效的 Base64 密文');
}
// 2. 主鑰解密
$obfuscated = $this->xorWithKey($decoded, $this->cryptKey);
// 3. 還原明文 (跳過會話鑰字符)
$plaintext = '';
for ($i = 0; $i < strlen($obfuscated); $i += 2) {
// $obfuscated[$i] 是會話鑰字符,跳過
if (isset($obfuscated[$i + 1])) {
$plaintext .= $obfuscated[$i + 1] ^ $obfuscated[$i];
}
}
return $plaintext;
}
/**
* 核心 XOR 混淆算法
*/
private function xorWithKey(string $data, string $key): string {
$keyHash = md5($key); // 固定 32 字符
$result = '';
$keyLen = strlen($keyHash);
for ($i = 0; $i < strlen($data); $i++) {
$result .= $data[$i] ^ $keyHash[$i % $keyLen];
}
return $result;
}
/**
* 靜態快捷方法
*/
public static function quickEncrypt(string $data, string $key): string {
return (new self($key))->encrypt($data);
}
public static function quickDecrypt(string $data, string $key): string {
return (new self($key))->decrypt($data);
}
}
// ===== 使用示例 =====
$crypt = new SysCrypt('MySecretKey2024!@#'); // 生產環境請從環境變量讀取
$original = 'user_id=10086&api_key=sk_live_xxx×tamp=' . time();
echo "原文: $original\n";
$encrypted = $crypt->encrypt($original);
echo "密文: $encrypted\n";
$decrypted = $crypt->decrypt($encrypted);
echo "解密: $decrypted\n";
echo "驗證: " . ($original === $decrypted ? '通過 ✓' : '失敗 ✗') . "\n";
?>
| 層面 | 現狀 | 風險 | 改進方案 |
|---|---|---|---|
| 算法強度 | 自定義 XOR 流加密 | 抗密文分析能力弱,不抗已知明文攻擊 | 生產環境改用 openssl_encrypt($data, 'AES-256-GCM', $key, OPENSSL_RAW_DATA, $iv, $tag) |
| 密鑰管理 | 硬編碼/構造函數傳入 | 代碼洩露導致密鑰洩露 | 使用環境變量、密鑰管理服務(Vault)、HSM |
| 完整性驗證 | 無 | 密文可被篡改解密出垃圾數據 | 添加 HMAC-SHA256 簽名或使用 AEAD 模式 (GCM/CCM) |
| 重放攻擊 | 無防護 | 同一密文可重複使用 | 在明文中包含時間戳/Nonce,解密後驗證時效性 |
| 隨機性 | random_bytes() CSPRNG | 會話鑰生成安全 | 保持現狀 |
<?php
class ModernCrypt {
private string $key; // 32 字節 (256 bit)
private int $ivLen = 12; // GCM 推薦 12 字節 IV
private int $tagLen = 16; // 認證標籤 16 字節
public function __construct(string $keyBase64) {
$this->key = base64_decode($keyBase64);
if (strlen($this->key) !== 32) {
throw new InvalidArgumentException('密鑰必須為 32 字節 Base64 編碼');
}
}
public function encrypt(string $plaintext, string $associatedData = ''): string {
$iv = random_bytes($this->ivLen);
$tag = '';
$ciphertext = openssl_encrypt(
$plaintext,
'AES-256-GCM',
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag,
$associatedData
);
if ($ciphertext === false) {
throw new Exception('加密失敗: ' . openssl_error_string());
}
// 組合: IV(12) + Ciphertext + Tag(16) -> Base64
return base64_encode($iv . $ciphertext . $tag);
}
public function decrypt(string $ciphertextB64, string $associatedData = ''): string {
$data = base64_decode($ciphertextB64, true);
if ($data === false || strlen($data) < $this->ivLen + $this->tagLen) {
throw new Exception('無效密文格式');
}
$iv = substr($data, 0, $this->ivLen);
$tag = substr($data, -$this->tagLen);
$ciphertext = substr($data, $this->ivLen, -$this->tagLen);
$plaintext = openssl_decrypt(
$ciphertext,
'AES-256-GCM',
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag,
$associatedData
);
if ($plaintext === false) {
throw new Exception('解密失敗/數據被篡改: ' . openssl_error_string());
}
return $plaintext;
}
}
// 生成密鑰 (僅執行一次,安全保存)
$key = base64_encode(random_bytes(32));
echo "請妥善保存此密鑰: $key\n";
// 使用
$crypt = new ModernCrypt($key);
$data = '{"user_id":10086,"role":"admin","exp":' . (time()+3600) . '}';
$enc = $crypt->encrypt($data, 'api_v1'); // 關聯數據綁定上下文
echo "加密: $enc\n";
echo "解密: " . $crypt->decrypt($enc, 'api_v1') . "\n";
?>
<?php
// config.encrypted.php
return [
'database' => [
'host' => 'localhost',
'password' => 'ENC:eyJpdiI6I...', // 加密後的密碼
],
'redis' => [
'password' => 'ENC:abc123...',
],
];
// 運行時解密
function decryptConfig(array $config, ModernCrypt $crypt): array {
array_walk_recursive($config, function(&$value) use ($crypt) {
if (is_string($value) && str_starts_with($value, 'ENC:')) {
$value = $crypt->decrypt(substr($value, 4));
}
});
return $config;
}
?>
<?php
function signUrl(string $route, array $params, ModernCrypt $crypt, int $ttl = 3600): string {
$params['_ts'] = time();
$params['_exp'] = time() + $ttl;
$payload = http_build_query($params);
$encrypted = $crypt->encrypt($payload);
return $route . '?token=' . urlencode($encrypted);
}
function verifyUrlToken(string $token, ModernCrypt $crypt): ?array {
try {
$payload = $crypt->decrypt(urldecode($token));
parse_str($payload, $params);
if (($params['_exp'] ?? 0) < time()) {
return null; // 過期
}
unset($params['_ts'], $params['_exp']);
return $params;
} catch (Exception) {
return null; // 篡改或無效
}
}
// 生成帶簽名的下載鏈接
$url = signUrl('/download', ['file_id' => 123, 'user_id' => 456], $crypt, 1800);
// 輸出: /download?token=eyJpdiI6I...
// 驗證
$params = verifyUrlToken($_GET['token'] ?? '', $crypt);
if ($params) {
// 允許下載 $params['file_id']
}
?>
SysCrypt 類結構清晰,適合理解對稱加密原理ModernCrypt (AES-256-GCM),提供機密性、完整性、認證性三重保障加密只是資安拼圖的一塊,還需結合 HTTPS 傳輸、最小權限原則、審計日誌、入侵檢測等纵深防禦體系。