PHP 数据库敏感数据加密存储方案

```html PHP 数据库敏感数据加密存储方案

PHP 数据库敏感数据加密存储方案

在用户注册、支付、身份认证等场景中,密码、手机号、身份证号、银行卡号等属于敏感数据,绝不可明文存储于数据库。PHP 提供了成熟的安全工具链,本文介绍一种符合 OWASP 和 GDPR 原则的端到端加密实践方案。

✅ 核心原则

  • 加密而非哈希:对需可逆读取的字段(如手机号)使用 AES-256-GCM 加密;
  • 密钥分离:主密钥(KEK)存于环境变量或密钥管理服务(如 AWS KMS),不硬编码;
  • 随机 IV + 认证标签:使用 GCM 模式确保机密性与完整性;
  • 字段级加密:仅加密敏感列(如 encrypted_phone),非整表加密。

🔐 示例:手机号加密存储

<?php
// config.php —— 从环境变量加载密钥(推荐使用 dotenv 或系统 env)
$key = base64_decode($_ENV['APP_ENCRYPTION_KEY'] ?? 'your-32-byte-base64-key-here==');
if (mb_strlen($key, '8bit') !== 32) {
    throw new RuntimeException('Invalid encryption key length');
}

// 加密函数(AES-256-GCM)
function encryptField(string $plaintext, string $key): string
{
    $iv = random_bytes(12); // GCM 推荐 12 字节 IV
    $tag = '';
    $ciphertext = openssl_encrypt(
        $plaintext,
        'aes-256-gcm',
        $key,
        OPENSSL_RAW_DATA,
        $iv,
        $tag,
        '', // aad(可选)
        16 // tag length
    );
    if ($ciphertext === false) {
        throw new RuntimeException('Encryption failed: ' . openssl_error_string());
    }
    return base64_encode($iv . $tag . $ciphertext);
}

// 解密函数
function decryptField(string $encrypted, string $key): string
{
    $data = base64_decode($encrypted);
    if (strlen($data) < 28) {
        throw new RuntimeException('Invalid encrypted data length');
    }
    $iv = substr($data, 0, 12);
    $tag = substr($data, 12, 16);
    $ciphertext = substr($data, 28);
    
    $plaintext = openssl_decrypt(
        $ciphertext,
        'aes-256-gcm',
        $key,
        OPENSSL_RAW_DATA,
        $iv,
        $tag
    );
    if ($plaintext === false) {
        throw new RuntimeException('Decryption failed: ' . openssl_error_string());
    }
    return $plaintext;
}

// 使用示例
$phone = '13812345678';
$encrypted = encryptField($phone, $key);
echo "加密后: $encrypted\n"; // 存入数据库 encrypted_phone 字段

$decrypted = decryptField($encrypted, $key);
echo "解密后: $decrypted\n"; // 输出: 13812345678
?>

⚠️ 重要提醒

❌ 切勿使用 md5()、sha1() 或未加盐 hash 存储可逆敏感数据;
❌ 避免自研加密逻辑;优先使用 OpenSSL 扩展(PHP ≥ 7.1);
✅ 密钥必须定期轮换,并记录审计日志;
✅ 生产环境禁用 display_errors,防止密钥/错误信息泄露。

最后,建议结合 Laravel 的 encrypt cast 或 Symfony 的 Security Component 进行工程化封装,提升可维护性与安全性。

安全不是功能,而是设计起点。

```