本文目录导读:

- 使用 OpenSSL 扩展(推荐用于通用加密)
- 使用 Sodium 扩展(现代安全方案,PHP 7.2+)
- 可逆哈希(不适合加密,仅用于密码存储)
- 数据库字段级加密(MySQL AES)
- 配置文件加密
- 最佳实践建议
在PHP项目中实现加密解密,有多种方法,具体选择取决于你的需求(如加密强度、性能、是否可逆等),以下是几种常见且安全的实现方式:
使用 OpenSSL 扩展(推荐用于通用加密)
对称加密(AES-256-GCM)
<?php
/**
* AES-256-GCM 加密解密示例
* 适用于:数据传输加密、敏感信息存储
* 特点:带认证标签,防篡改
*/
class AesGcmEncryption {
private string $key;
private int $tagLength = 16; // 128位认证标签
public function __construct(string $key) {
// 确保密钥长度为32字节(256位)
$this->key = hash('sha256', $key, true);
}
/**
* 加密
* @param string $plaintext 明文
* @return string Base64编码的加密字符串(包含IV和认证标签)
*/
public function encrypt(string $plaintext): string {
$iv = openssl_random_pseudo_bytes(12); // 96位IV
$tag = '';
$ciphertext = openssl_encrypt(
$plaintext,
'aes-256-gcm',
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag,
'',
$this->tagLength
);
// 组合IV + 密文 + 认证标签
$encoded = base64_encode($iv . $ciphertext . $tag);
return $encoded;
}
/**
* 解密
* @param string $ciphertext Base64编码的加密字符串
* @return string|false 解密成功返回明文,失败返回false
*/
public function decrypt(string $ciphertext): string|false {
$decoded = base64_decode($ciphertext);
// 提取IV、密文和认证标签
$iv = substr($decoded, 0, 12);
$tag = substr($decoded, -$this->tagLength);
$encryptedData = substr($decoded, 12, -$this->tagLength);
$plaintext = openssl_decrypt(
$encryptedData,
'aes-256-gcm',
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag
);
return $plaintext;
}
}
// 使用示例
$encryption = new AesGcmEncryption('my-secret-key-12345');
$encrypted = $encryption->encrypt('Hello, World!');
echo "加密后: " . $encrypted . "\n";
$decrypted = $encryption->decrypt($encrypted);
echo "解密后: " . $decrypted . "\n";
使用 Sodium 扩展(现代安全方案,PHP 7.2+)
<?php
/**
* Sodium 加密解密(现代且安全的加密库)
* 适用于:需要最高安全标准的应用
* 特点:简单易用,内置防篡改
*/
class SodiumEncryption {
/**
* 加密
* @param string $plaintext 明文
* @param string $key 密钥(必须32字节)
* @return string Base64编码的加密结果
*/
public static function encrypt(string $plaintext, string $key): string {
// 生成随机nonce
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
// 加密
$ciphertext = sodium_crypto_secretbox($plaintext, $nonce, $key);
// 组合nonce + 密文
return base64_encode($nonce . $ciphertext);
}
/**
* 解密
* @param string $ciphertext Base64编码的加密字符串
* @param string $key 密钥(必须32字节)
* @return string|false 解密成功返回明文,失败返回false
*/
public static function decrypt(string $ciphertext, string $key): string|false {
$decoded = base64_decode($ciphertext);
if (mb_strlen($decoded, '8bit') < SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES) {
return false;
}
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
$plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
// 清除敏感数据
sodium_memzero($nonce);
return $plaintext;
}
/**
* 生成安全随机密钥
* @return string 32字节的密钥
*/
public static function generateKey(): string {
return random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
}
}
// 使用示例
$key = SodiumEncryption::generateKey();
echo "密钥 (Base64): " . base64_encode($key) . "\n";
$encrypted = SodiumEncryption::encrypt('Hello, World!', $key);
echo "加密后: " . $encrypted . "\n";
$decrypted = SodiumEncryption::decrypt($encrypted, $key);
echo "解密后: " . $decrypted . "\n";
可逆哈希(不适合加密,仅用于密码存储)
<?php
/**
* 密码哈希(不可逆,用于存储密码)
* 注意:这不是加密,是单向哈希
*/
class PasswordHasher {
/**
* 创建密码哈希
*/
public static function hash(string $password): string {
return password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
}
/**
* 验证密码
*/
public static function verify(string $password, string $hash): bool {
return password_verify($password, $hash);
}
}
// 使用示例
$hash = PasswordHasher::hash('my-secret-password');
echo "密码哈希: " . $hash . "\n";
$isValid = PasswordHasher::verify('my-secret-password', $hash);
echo "密码验证: " . ($isValid ? '有效' : '无效') . "\n";
数据库字段级加密(MySQL AES)
<?php
/**
* 数据库字段加密(使用MySQL内置函数)
* 适用于:需要在数据库层面进行加密的场景
*/
class DatabaseEncryption {
private PDO $pdo;
private string $encryptionKey;
public function __construct(PDO $pdo, string $key) {
$this->pdo = $pdo;
$this->encryptionKey = $key;
}
/**
* 插入加密数据
*/
public function insertEncryptedData(string $table, string $column, string $plaintext): bool {
$sql = "INSERT INTO $table ($column) VALUES (AES_ENCRYPT(:plaintext, :key))";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([
':plaintext' => $plaintext,
':key' => $this->encryptionKey
]);
}
/**
* 查询解密数据
*/
public function getDecryptedData(string $table, string $column, int $id): ?string {
$sql = "SELECT AES_DECRYPT($column, :key) FROM $table WHERE id = :id";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([
':key' => $this->encryptionKey,
':id' => $id
]);
$result = $stmt->fetchColumn();
return $result ?: null;
}
}
配置文件加密
<?php
/**
* 配置文件加密(适用于.env文件等)
* 特点:简单,适合存储API密钥等敏感配置
*/
class ConfigEncryption {
private string $key;
private string $method = 'aes-256-cbc';
public function __construct(string $key) {
$this->key = hash('sha256', $key, true);
}
/**
* 加密配置文件值
*/
public function encryptConfigValue(string $value): string {
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->method));
$encrypted = openssl_encrypt($value, $this->method, $this->key, 0, $iv);
return base64_encode($iv . '::' . $encrypted);
}
/**
* 解密配置文件值
*/
public function decryptConfigValue(string $encryptedValue): string|false {
$data = base64_decode($encryptedValue);
$parts = explode('::', $data, 2);
if (count($parts) !== 2) {
return false;
}
[$iv, $encrypted] = $parts;
return openssl_decrypt($encrypted, $this->method, $this->key, 0, $iv);
}
}
// 使用示例
$configEnc = new ConfigEncryption('my-config-key');
$encryptedApiKey = $configEnc->encryptConfigValue('sk-xxxxx');
echo "加密的API密钥: " . $encryptedApiKey . "\n";
$decryptedApiKey = $configEnc->decryptConfigValue($encryptedApiKey);
echo "解密的API密钥: " . $decryptedApiKey . "\n";
最佳实践建议
-
选择合适的加密方法:
- 数据加密:使用 AES-256-GCM 或 Sodium
- 密码存储:使用 password_hash()(不可逆)
- 临时令牌:使用 openssl_random_pseudo_bytes()
-
密钥管理:
- 将密钥存储在环境变量或密钥管理服务中
- 不要硬编码密钥到代码中
- 定期轮换密钥
-
安全注意事项:
- 始终使用随机IV/nonce
- 使用认证加密(如GCM模式)防止篡改
- 不要使用过时的算法(如MD5、SHA1、DES)
- 对于敏感数据,考虑在传输层使用HTTPS
-
性能考虑:
- 对称加密比非对称加密快得多
- Sodium比OpenSSL在某些操作上更快
- 对于批量加密,考虑使用流式加密
选择哪种方法取决于你的具体需求:如果需要跨平台兼容,选OpenSSL;如果追求最高安全性且PHP 7.2+,选Sodium;如果只是密码存储,用password_hash()。