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

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

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

在用户注册、支付、身份认证等场景中,密码、身份证号、手机号、银行卡号等属于敏感数据,绝不可明文存储于数据库。PHP 提供了成熟的安全工具链,结合现代加密实践,可构建可靠的数据保护机制。

✅ 推荐方案:AES-256-GCM 对称加密 + 密钥管理

相比过时的 mcrypt 或简单哈希(仅适用于密码),AES-256-GCM 提供机密性 + 完整性验证,且 PHP 7.1+ 原生支持 openssl_encrypt(),安全高效。

🔐 核心实现示例

<?php
// 配置:建议从环境变量或密钥管理系统加载
$encryptionKey = hex2bin(getenv('APP_ENCRYPTION_KEY') ?: '0123456789abcdef0123456789abcdef'); // 32字节 AES-256 密钥
$ivLength = openssl_cipher_iv_length('aes-256-gcm');

// ✅ 加密函数(用于写入数据库)
function encryptSensitive(string $plaintext, string $key): string
{
    $iv = random_bytes($ivLength);
    $tag = '';
    $ciphertext = openssl_encrypt(
        $plaintext,
        'aes-256-gcm',
        $key,
        OPENSSL_RAW_DATA,
        $iv,
        $tag,
        '', // aad(可选附加认证数据,如用户ID)
        16 // tag长度(推荐12–16字节)
    );

    if ($ciphertext === false) {
        throw new RuntimeException('Encryption failed: ' . openssl_error_string());
    }

    return base64_encode($iv . $tag . $ciphertext); // IV + TAG + CIPHERTEXT(base64编码便于存储)
}

// ✅ 解密函数(用于读取使用)
function decryptSensitive(string $encrypted, string $key): string
{
    $data = base64_decode($encrypted);
    if (strlen($data) < $ivLength + 16) {
        throw new RuntimeException('Invalid encrypted data length');
    }

    $iv = substr($data, 0, $ivLength);
    $tag = substr($data, $ivLength, 16);
    $ciphertext = substr($data, $ivLength + 16);

    $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;
}

// 🧪 使用示例
$cardNumber = '6228 4800 0000 0000 000';
$encrypted = encryptSensitive($cardNumber, $encryptionKey);
echo "加密后: {$encrypted}\n";

$decrypted = decryptSensitive($encrypted, $encryptionKey);
echo "解密后: {$decrypted}\n"; // 输出原始卡号
?>

⚠️ 关键安全提醒

  • 密钥绝不硬编码:使用环境变量(如 .env)、Vault 或云 KMS 管理;禁止提交至 Git。
  • 区分用途:密码应使用 password_hash()(bcrypt)单向哈希;而银行卡、证件号等需双向加解密,才用 AES。
  • 字段级加密:仅加密真正敏感字段(如 user_id_card, payment_card_no),避免全表加密影响性能与查询能力。
  • 审计与轮换:定期轮换密钥,并设计兼容旧密钥的解密逻辑(如添加密钥版本标识字段)。

安全不是功能,而是贯穿开发全周期的习惯。从今天起,让每一行敏感数据,都穿上加密的铠甲。

```