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

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

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

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

✅ 核心原则

  • 加密而非哈希:对需可逆读取的字段(如手机号)使用 AES-256-GCM 加密;
  • 密钥分离:主密钥(KEK)存于环境变量或密钥管理服务(如 AWS KMS),不硬编码;
  • 随机 IV + 认证标签:防止重放与篡改,确保完整性;
  • 应用层加密:数据库仅存储密文,DBA 也无法直接读取明文。

🔐 实现示例(PHP 8.1+)

<?php
// config.php —— 密钥从环境变量安全加载
$encryptionKey = $_ENV['APP_ENCRYPTION_KEY'] ?? null;
if (!$encryptionKey || strlen($encryptionKey) !== 32) {
    throw new RuntimeException('Invalid or missing encryption key (32-byte required)');
}

// 加密函数(AES-256-GCM)
function encrypt(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 decrypt(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';
$encryptedPhone = encrypt($phone, $encryptionKey);
echo "密文: $encryptedPhone\n"; // 存入数据库 users.phone_enc

$decryptedPhone = decrypt($encryptedPhone, $encryptionKey);
echo "解密: $decryptedPhone\n"; // 输出:13812345678
?>

⚠️ 安全提醒

切勿:使用 md5/sha1 加密敏感字段;将密钥写死在代码中;忽略 IV/Tag 的唯一性;在日志中打印密文或密钥。

建议:结合 ext-sodium(PHP 7.2+)使用 sodium_crypto_aead_xchacha20poly1305_ietf_encrypt() 获取更高安全性;定期轮换加密密钥;对加密字段添加数据库注释(如 /* ENCRYPTED: AES-256-GCM */)便于团队协作。

安全不是功能,而是设计起点。从今天起,让每一行敏感数据,都拥有自己的“数字保险箱”。

```