PHP 数据库敏感数据加密存储方案
在用户注册、支付、身份认证等场景中,密码、手机号、身份证号、银行卡号等属于敏感数据,绝不可明文存储于数据库。PHP 提供了成熟的安全工具链,本文介绍一种符合 OWASP 和 GDPR 原则的端到端加密实践方案。
✅ 核心原则
- 加密而非哈希:对需可逆读取的字段(如手机号)使用 AES-256-GCM 加密;
- 密钥分离:主密钥(KEK)存于环境变量或密钥管理服务(如 AWS KMS),不与代码/数据库共存;
- 随机 IV + 认证标签:使用 GCM 模式保障机密性与完整性;
- 字段级加密:仅加密敏感列(如
encrypted_phone),非整表加密。
🔐 示例:手机号 AES-256-GCM 加密类
<?php
class SecureDataEncryptor
{
private string $key;
public function __construct(string $encryptionKey)
{
// 确保密钥为 32 字节(AES-256)
$this->key = hash('sha256', $encryptionKey, true);
}
public function encrypt(string $plaintext): string
{
$iv = random_bytes(12); // GCM 推荐 96-bit IV
$ciphertext = openssl_encrypt(
$plaintext,
'aes-256-gcm',
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag,
'',
16 // 认证标签长度
);
if ($ciphertext === false) {
throw new RuntimeException('Encryption failed: ' . openssl_error_string());
}
return base64_encode($iv . $tag . $ciphertext);
}
public function decrypt(string $encrypted): string
{
$data = base64_decode($encrypted);
$iv = substr($data, 0, 12);
$tag = substr($data, 12, 16);
$ciphertext = substr($data, 28);
$plaintext = openssl_decrypt(
$ciphertext,
'aes-256-gcm',
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag
);
if ($plaintext === false) {
throw new RuntimeException('Decryption failed: ' . openssl_error_string());
}
return $plaintext;
}
}
// 使用示例
$encryptor = new SecureDataEncryptor($_ENV['APP_ENCRYPTION_KEY'] ?? 'your-secure-32-byte-key-here');
$encryptedPhone = $encryptor->encrypt('13812345678');
// 存入数据库(PDO 示例)
$stmt = $pdo->prepare("INSERT INTO users (name, encrypted_phone) VALUES (?, ?)");
$stmt->execute(['张三', $encryptedPhone]);
// 查询时解密
$stmt = $pdo->prepare("SELECT encrypted_phone FROM users WHERE id = ?");
$stmt->execute([1]);
$encrypted = $stmt->fetchColumn();
$phone = $encryptor->decrypt($encrypted); // → "13812345678"
?>
⚠️ 重要提醒:
• 密钥严禁硬编码!请通过
.env(配合 vault 或 docker secrets)注入;
• 加密字段建议使用 TEXT 类型(AES-GCM 后 Base64 编码约增长 33%);
• 避免在 SQL 查询中直接解密(如 WHERE decrypt(encrypted_phone) = ?),会破坏索引且暴露风险。
安全无小事。每一次对敏感字段的加密,都是对用户信任的郑重承诺。从今天起,让明文成为历史 —— 用标准、可控、可审计的方式守护每一份数据。
```