PHP 数据库敏感数据加密存储方案
在用户注册、支付、身份认证等场景中,密码、身份证号、手机号、银行卡号等属于敏感数据,绝不可明文存储于数据库。PHP 提供了成熟的安全工具链,结合现代加密实践,可构建可靠的数据保护机制。
✅ 推荐方案:AES-256-GCM + 密钥分离
相比过时的 mcrypt 或简单哈希(仅适用于密码),对称加密 + 随机 IV + 认证标签 是存储可逆敏感字段(如手机号、地址)的黄金标准。PHP 7.1+ 原生支持 openssl_encrypt(),推荐使用 AES-256-GCM 模式——它同时提供机密性与完整性校验。
🔐 安全实践要点
- 密钥绝不硬编码:存于环境变量或专用密钥管理服务(如 HashiCorp Vault)
- 每次加密生成唯一 IV(初始化向量),并与密文一同存储
- 密文需包含认证标签(tag),解密时强制验证,防止篡改
- 数据库字段类型建议:
VARCHAR(512)(Base64 编码后长度可控)
💻 示例代码(PHP 8.1+)
<?php
// 加密服务类(简化版)
class SecureDataCryptor
{
private string $cipher = 'aes-256-gcm';
private string $key;
public function __construct(string $key)
{
// 密钥必须为 32 字节(AES-256)
if (strlen($key) !== 32) {
throw new InvalidArgumentException('AES-256 key must be exactly 32 bytes.');
}
$this->key = $key;
}
public function encrypt(string $plaintext): array
{
$iv = random_bytes(openssl_cipher_iv_length($this->cipher));
$tag = '';
$ciphertext = openssl_encrypt(
$plaintext,
$this->cipher,
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag,
'',
16 // tag length
);
if ($ciphertext === false) {
throw new RuntimeException('Encryption failed: ' . openssl_error_string());
}
return [
'ciphertext' => base64_encode($ciphertext),
'iv' => base64_encode($iv),
'tag' => base64_encode($tag),
];
}
public function decrypt(array $data): string
{
$ciphertext = base64_decode($data['ciphertext']);
$iv = base64_decode($data['iv']);
$tag = base64_decode($data['tag']);
$plaintext = openssl_decrypt(
$ciphertext,
$this->cipher,
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag
);
if ($plaintext === false) {
throw new RuntimeException('Decryption failed or data tampered: ' . openssl_error_string());
}
return $plaintext;
}
}
// 使用示例
$key = $_ENV['APP_ENCRYPTION_KEY'] ?? '32-byte-secret-key-for-aes256!'; // ✅ 从 .env 或 secrets 注入
$cryptor = new SecureDataCryptor($key);
// 存储前加密
$phone = '13800138000';
$encrypted = $cryptor->encrypt($phone);
// → ['ciphertext'=>'...', 'iv'=>'...', 'tag'=>'...']
// 插入数据库:INSERT INTO users (encrypted_phone, phone_iv, phone_tag) VALUES (?, ?, ?)
// 读取后解密
$decryptedPhone = $cryptor->decrypt([
'ciphertext' => $encrypted['ciphertext'],
'iv' => $encrypted['iv'],
'tag' => $encrypted['tag'],
]); // → '13800138000'
?>
⚠️ 重要提醒:密码请始终使用
password_hash() + password_verify()(bcrypt/Argon2),不可加密存储;本方案仅适用于需后续解密的业务字段(如客服查看用户手机号)。
安全无小事。加密只是纵深防御的一环——还需配合最小权限数据库账户、HTTPS 传输、定期密钥轮换及审计日志。用对工具,更要敬畏风险。
```