加密是數據安全的基石。本文遞進式介紹三種 PHP 加密方案:字符映射替換(教學用)、自定義流加密(不推薦生產)、標準 AES-GCM(生產首選),並強調密鑰管理、IV 隨機性、完整性驗證等核心安全原則。
<?php
/**
* 簡單字符替換加密(極不安全,僅作演示)
*/
class SimpleSubstitutionCipher
{
private string $alphabet = 'abA!c1dB#ef2@Cg$h%iD_3jkl^E:m}4n.o{&F*p)5q(G-r[sH]6tuIv7w+Jxy8z9K0';
private string $cipher = 'zAy%0Bx+1C$wDv^Eu2-t3(F{sr&G4q_pH5*on6I)m:l7.Jk]j8K}ih@gf9#ed!cb[';
public function encrypt(string $str): string
{
$map = array_combine(str_split($this->alphabet), str_split($this->cipher));
return strtr($str, $map);
}
public function decrypt(string $str): string
{
$map = array_combine(str_split($this->cipher), str_split($this->alphabet));
return strtr($str, $map);
}
}
// 使用
$cipher = new SimpleSubstitutionCipher();
$enc = $cipher->encrypt('www.example.com');
$dec = $cipher->decrypt($enc);
echo "加密: $enc\n解密: $dec\n";
?>
缺陷:單表替換密碼,頻率分析即可破解;無完整性校驗;密鑰硬編碼;不支持二進制數據。
<?php
/**
* 基於 MD5 衍生密鑰流的 XOR 加密(教學展示,生產請用 AES)
*/
class LegacyStreamCipher
{
public function encrypt(string $plaintext, string $key): string
{
$encryptKey = md5(mt_rand(0, 32000)); // 每次加密生成隨機密鑰流種子
$ctr = 0;
$ciphertext = '';
for ($i = 0; $i < strlen($plaintext); $i++) {
$ctr = $ctr === strlen($encryptKey) ? 0 : $ctr;
$ciphertext .= $encryptKey[$ctr] . ($plaintext[$i] ^ $encryptKey[$ctr++]);
}
// 二次加密:使用用戶密鑰衍生的密鑰流
return base64_encode($this->xorCipher($ciphertext, md5($key)));
}
public function decrypt(string $ciphertextB64, string $key): string
{
$ciphertext = base64_decode($ciphertextB64);
$ciphertext = $this->xorCipher($ciphertext, md5($key));
$plaintext = '';
for ($i = 0; $i < strlen($ciphertext); $i++) {
$k = $ciphertext[$i];
$plaintext .= $ciphertext[++$i] ^ $k;
}
return $plaintext;
}
private function xorCipher(string $data, string $key): string
{
$ctr = 0;
$out = '';
for ($i = 0; $i < strlen($data); $i++) {
$ctr = $ctr === strlen($key) ? 0 : $ctr;
$out .= $data[$i] ^ $key[$ctr++];
}
return $out;
}
}
?>
風險:MD5 已被碰撞攻擊破解;自定義密鑰衍生非標準;無認證加密(AEAD),密文可被篡改;隨機數來源 mt_rand 非加密安全。
PHP 7.1+ 通过 OpenSSL 擴展原生支持 AES-GCM(認證加密),同時提供機密性、完整性、真實性。
<?php
/**
* 現代化 AES-256-GCM 加密工具類
* 需 PHP 7.1+ 與 OpenSSL 擴展
*/
class AesGcmCipher
{
private const CIPHER = 'aes-256-gcm';
private const KEY_LENGTH = 32; // 256 bits
private const IV_LENGTH = 12; // 96 bits (GCM 推薦)
private const TAG_LENGTH = 16; // 128 bits
/**
* 加密
* @param string $plaintext 明文
* @param string $keyBase64 Base64 編碼的 32 字節主密鑰
* @param string|null $aad 附加認證數據(可選,如用戶ID、時間戳)
* @return string Base64 編碼的密文 (IV + Ciphertext + Tag)
*/
public static function encrypt(string $plaintext, string $keyBase64, ?string $aad = null): string
{
$key = base64_decode($keyBase64);
if (strlen($key) !== self::KEY_LENGTH) {
throw new \InvalidArgumentException('密鑰必須為 32 字節 (Base64 編碼後 44 字符)');
}
$iv = random_bytes(self::IV_LENGTH);
$tag = null;
$ciphertext = openssl_encrypt(
$plaintext,
self::CIPHER,
$key,
OPENSSL_RAW_DATA,
$iv,
$tag,
$aad ?? ''
);
if ($ciphertext === false) {
throw new \RuntimeException('加密失敗: ' . openssl_error_string());
}
// 組合:IV (12) + Ciphertext + Tag (16)
return base64_encode($iv . $ciphertext . $tag);
}
/**
* 解密
* @param string $payloadBase64 Base64 編碼的密文 (IV + Ciphertext + Tag)
* @param string $keyBase64 Base64 編碼的 32 字節主密鑰
* @param string|null $aad 附加認證數據(需與加密時一致)
* @return string 明文
*/
public static function decrypt(string $payloadBase64, string $keyBase64, ?string $aad = null): string
{
$key = base64_decode($keyBase64);
$payload = base64_decode($payloadBase64);
$iv = substr($payload, 0, self::IV_LENGTH);
$tag = substr($payload, -self::TAG_LENGTH);
$ciphertext = substr($payload, self::IV_LENGTH, -self::TAG_LENGTH);
$plaintext = openssl_decrypt(
$ciphertext,
self::CIPHER,
$key,
OPENSSL_RAW_DATA,
$iv,
$tag,
$aad ?? ''
);
if ($plaintext === false) {
throw new \RuntimeException('解密失敗(密文被篡改或密鑰錯誤)');
}
return $plaintext;
}
// 生成隨機主密鑰(僅需執行一次,安全存儲)
public static function generateKey(): string
{
return base64_encode(random_bytes(self::KEY_LENGTH));
}
}
// === 使用示例 ===
// 1. 生成密鑰(僅執行一次,存入環境變量或密鑰管理系統)
// $masterKey = AesGcmCipher::generateKey(); // 例如: "x7K9mP2qR5vW8zA3..."
$masterKey = 'x7K9mP2qR5vW8zA3...'; // 從配置讀取
// 2. 加密
$plaintext = '敏感數據:用戶身份證號 110101199001011234';
$encrypted = AesGcmCipher::encrypt($plaintext, $masterKey, 'user_12345');
echo "加密後: $encrypted\n";
// 3. 解密
$decrypted = AesGcmCipher::decrypt($encrypted, $masterKey, 'user_12345');
echo "解密後: $decrypted\n";
?>
random_bytes() 生成隨機 IV,同一密鑰下 IV 絕不重複。加密只是安全拼圖的一塊。傳輸層需 TLS 1.3、存儲層需磁盤加密、應用層需最小權限原則、審計日誌與入侵檢測體系才能構建纵深防御。