專案常需加密敏感資料 (Email、ID、Token) 傳遞或儲存。本文提供一個對稱加密類別,採用自定義演算法 + Base64,並說明為何生產環境應改用 OpenSSL AES。
<?php
class SysCrypt {
private string $crypt_key;
public function __construct(string $crypt_key = 'default_secret_key') {
$this->crypt_key = $crypt_key;
}
public function encrypt(string $plaintext): string {
// 產生隨機加密金鑰
$encrypt_key = md5((string)random_int(0, 32000));
$ctr = 0;
$tmp = '';
// XOR 加密 + 插入金鑰字元
for ($i = 0; $i < strlen($plaintext); $i++) {
$ctr = ($ctr === strlen($encrypt_key)) ? 0 : $ctr;
$tmp .= $encrypt_key[$ctr] . ($plaintext[$i] ^ $encrypt_key[$ctr++]);
}
// 二次加密 + Base64
return base64_encode($this->xorCipher($tmp, $this->crypt_key));
}
public function decrypt(string $ciphertext): string {
// Base64 解碼 -> 二次解密 -> 還原 XOR
$data = $this->xorCipher(base64_decode($ciphertext), $this->crypt_key);
$tmp = '';
for ($i = 0; $i < strlen($data); $i++) {
$md5_char = $data[$i];
$tmp .= $data[++$i] ^ $md5_char;
}
return $tmp;
}
private function xorCipher(string $data, string $key): string {
$key = md5($key);
$ctr = 0;
$result = '';
for ($i = 0; $i < strlen($data); $i++) {
$ctr = ($ctr === strlen($key)) ? 0 : $ctr;
$result .= $data[$i] ^ $key[$ctr++];
}
return $result;
}
public function __destruct() {
$this->crypt_key = '';
}
}
// 使用範例
$crypt = new SysCrypt('my_secret_key_2024');
$original = 'user@example.com';
$encrypted = $crypt->encrypt($original);
$decrypted = $crypt->decrypt($encrypted);
echo "原文: $original\n";
echo "密文: $encrypted\n";
echo "解密: $decrypted\n";
// 原文: user@example.com
// 密文: BS8DbFU6AioCNFFlVGZQMgRGBDUOYlEzBXoAZgo0Bjk=
// 解密: user@example.com
?>
md5(rand()) 熵值極低,可被暴力破解<?php
class SecureCrypt {
private string $key; // 32 bytes (256 bits)
private int $ivLength = 12; // GCM 建議 12 bytes
private int $tagLength = 16; // GCM 標籤 16 bytes
public function __construct(string $keyBase64) {
$this->key = base64_decode($keyBase64);
if (strlen($this->key) !== 32) {
throw new InvalidArgumentException('Key must be 32 bytes (256 bits) Base64 encoded');
}
}
public function encrypt(string $plaintext, string $associatedData = ''): string {
$iv = random_bytes($this->ivLength);
$ciphertext = openssl_encrypt(
$plaintext,
'aes-256-gcm',
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag,
$associatedData,
$this->tagLength
);
if ($ciphertext === false) {
throw new RuntimeException('Encryption failed: ' . openssl_error_string());
}
// 格式: IV (12) + Ciphertext + Tag (16) -> Base64
return base64_encode($iv . $ciphertext . $tag);
}
public function decrypt(string $payload, string $associatedData = ''): string {
$data = base64_decode($payload);
if (strlen($data) < $this->ivLength + $this->tagLength) {
throw new InvalidArgumentException('Invalid payload length');
}
$iv = substr($data, 0, $this->ivLength);
$tag = substr($data, -$this->tagLength);
$ciphertext = substr($data, $this->ivLength, -$this->tagLength);
$plaintext = openssl_decrypt(
$ciphertext,
'aes-256-gcm',
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag,
$associatedData
);
if ($plaintext === false) {
throw new RuntimeException('Decryption failed: authentication tag mismatch (data tampered?)');
}
return $plaintext;
}
}
// 使用
$keyBase64 = base64_encode(random_bytes(32)); // 安全儲存此金鑰!
$crypt = new SecureCrypt($keyBase64);
$encrypted = $crypt->encrypt('敏感資料', 'user_id:123'); // 附加關聯資料
$decrypted = $crypt->decrypt($encrypted, 'user_id:123');
?>
ENCRYPTION_KEY=base64_string,不進程式碼庫| 場景 | 推薦方案 |
|---|---|
| API Token、Session ID | random_bytes() + Base64 (無需解密) |
| 密碼儲存 | password_hash() / bcrypt / Argon2 (單向) |
| 資料庫敏感欄位 | AES-256-GCM (可逆) |
| URL 參數加密傳遞 | AES-256-GCM + Base64URL |
| 檔案加密 | AES-256-GCM 分塊或 age/rage 工具 |