本文目录导读:

在 PHP 中加密存储敏感数据(如密码、API密钥、个人信息等)是一个重要的安全实践,以下是几种常见且安全的加密存储方案:
密码加密(最常用)
使用 password_hash() 和 password_verify()(推荐)
// 加密(存储时)
$password = 'user_password_123';
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// 输出类似:$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi
// 存入数据库
$stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
$stmt->execute([$username, $hashedPassword]);
// 验证(登录时)
$inputPassword = 'user_input_password';
$storedHash = $row['password'];
if (password_verify($inputPassword, $storedHash)) {
echo "密码正确";
} else {
echo "密码错误";
}
// 检查是否需要重新哈希
if (password_needs_rehash($storedHash, PASSWORD_DEFAULT)) {
$newHash = password_hash($inputPassword, PASSWORD_DEFAULT);
// 更新数据库
}
对称加密(AES-256)
适用于需要解密的数据(如身份证号、银行卡等)。
// 定义加密类
class Crypto {
private $key;
private $cipher = 'aes-256-gcm';
public function __construct(string $encryptionKey) {
$this->key = hash('sha256', $encryptionKey, true);
}
// 加密
public function encrypt(string $plaintext): string {
$ivlen = openssl_cipher_iv_length($this->cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$tag = '';
$ciphertext = openssl_encrypt(
$plaintext,
$this->cipher,
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag
);
if ($ciphertext === false) {
throw new Exception('加密失败: ' . openssl_error_string());
}
// 将 IV 和 tag 一起存储
return base64_encode($iv . $tag . $ciphertext);
}
// 解密
public function decrypt(string $encryptedData): string {
$data = base64_decode($encryptedData);
$ivlen = openssl_cipher_iv_length($this->cipher);
$iv = substr($data, 0, $ivlen);
$tag = substr($data, $ivlen, 16); // GCM tag 是 16 字节
$ciphertext = substr($data, $ivlen + 16);
$plaintext = openssl_decrypt(
$ciphertext,
$this->cipher,
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag
);
if ($plaintext === false) {
throw new Exception('解密失败');
}
return $plaintext;
}
}
// 使用示例
$crypto = new Crypto('secret_key_here');
$encrypted = $crypto->encrypt('1234567890');
echo "加密后: " . $encrypted . "\n";
echo "解密后: " . $crypto->decrypt($encrypted);
非对称加密(RSA)
适用于双向通信或多方共享数据。
// 生成密钥对
$config = [
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
];
$keyPair = openssl_pkey_new($config);
// 提取公钥和私钥
openssl_pkey_export($keyPair, $privateKey);
$publicKey = openssl_pkey_get_details($keyPair)['key'];
// 加密(使用公钥)
function rsaEncrypt(string $data, string $publicKey): string {
$encrypted = '';
// 分段加密(因为 RSA 有长度限制)
$chunkSize = 245; // 2048位RSA最大加密长度
$dataChunks = str_split($data, $chunkSize);
foreach ($dataChunks as $chunk) {
$encryptedChunk = '';
openssl_public_encrypt($chunk, $encryptedChunk, $publicKey);
$encrypted .= $encryptedChunk;
}
return base64_encode($encrypted);
}
// 解密(使用私钥)
function rsaDecrypt(string $encryptedData, string $privateKey): string {
$data = base64_decode($encryptedData);
$decrypted = '';
$chunkSize = 256; // 2048位RSA最大解密长度
$dataChunks = str_split($data, $chunkSize);
foreach ($dataChunks as $chunk) {
$decryptedChunk = '';
openssl_private_decrypt($chunk, $decryptedChunk, $privateKey);
$decrypted .= $decryptedChunk;
}
return $decrypted;
}
// 使用示例
$data = "敏感数据";
$encrypted = rsaEncrypt($data, $publicKey);
$decrypted = rsaDecrypt($encrypted, $privateKey);
echo "解密结果: " . $decrypted;
哈希(单向加密)
适用于无需解密的场景(如验证数据完整性)。
// SHA-256 哈希
$hash = hash('sha256', $data);
// HMAC(带密钥的哈希)
$key = 'secret_key';
$hmac = hash_hmac('sha256', $data, $key);
// 加盐哈希
$salt = bin2hex(random_bytes(16));
$saltedHash = hash('sha256', $salt . $data);
// Bcrypt
$bcryptHash = password_hash($data, PASSWORD_BCRYPT, ['cost' => 12]);
// Argon2id(最安全)
$argon2Hash = password_hash($data, PASSWORD_ARGON2ID, [
'memory_cost' => 65536, // 64MB
'time_cost' => 4,
'threads' => 1
]);
数据库层面的加密
// MySQL AES 加密/解密 $query = "INSERT INTO users (name, ssn) VALUES (?, AES_ENCRYPT(?, ?))"; $stmt = $pdo->prepare($query); $stmt->execute([$name, $ssn, $encryptionKey]); $query = "SELECT AES_DECRYPT(ssn, ?) as ssn_decrypted FROM users WHERE id = ?"; $stmt = $pdo->prepare($query); $stmt->execute([$encryptionKey, $userId]); $row = $stmt->fetch(); echo $row['ssn_decrypted'];
完整的安全实践建议
class SecureData {
private $pdo;
private $encryptionKey;
public function __construct(\PDO $pdo, string $key) {
$this->pdo = $pdo;
$this->encryptionKey = $key;
}
// 安全存储密码
public function storePassword(int $userId, string $password): bool {
$hashed = password_hash($password, PASSWORD_DEFAULT);
$stmt = $this->pdo->prepare(
"UPDATE users SET password_hash = ? WHERE id = ?"
);
return $stmt->execute([$hashed, $userId]);
}
// 安全存储敏感数据
public function storeSensitiveData(int $userId, string $type, string $data): bool {
// 生成唯一的 IV
$iv = random_bytes(16);
$ciphertext = openssl_encrypt(
$data,
'aes-256-cbc',
$this->encryptionKey,
0,
$iv
);
$stmt = $this->pdo->prepare(
"INSERT INTO encrypted_data (user_id, data_type, iv, ciphertext)
VALUES (?, ?, ?, ?)"
);
return $stmt->execute([
$userId,
$type,
base64_encode($iv),
$ciphertext
]);
}
// 环境变量中的密钥(推荐)
// 不要硬编码密钥在代码中
public function getKey(): string {
return getenv('APP_ENCRYPTION_KEY') ?:
parse_ini_file('/secure/config.ini')['encryption_key'];
}
}
安全最佳实践总结
- 密码永远使用单向哈希:
password_hash()+password_verify() - 敏感数据使用 AES-256-GCM:带有认证标签,防篡改
- 密钥管理:
- 存储在环境变量或安全的密钥管理服务中
- 定期轮换密钥
- 不同用途使用不同密钥
- 使用 HTTPS:传输层加密
- 日志清理:不要记录敏感数据
- 备份安全:加密备份文件
选择哪种方案取决于你的需求:
- 无需解密 → 哈希(password_hash)
- 需要解密 → AES 对称加密
- 多方共享 → RSA 非对称加密
- 数据完整性验证 → HMAC