PHP 加密密钥管理与安全存储

```html PHP 加密密钥管理与安全存储

PHP 加密密钥管理与安全存储

在 PHP 应用中,正确管理加密密钥是保障数据安全的核心环节。密钥一旦泄露或硬编码,再强的加密算法也形同虚设。

❌ 常见错误实践

  • 硬编码密钥:将密钥直接写在源码中(如 $key = 'my-secret-key-123';
  • 存于版本库:密钥随代码提交至 Git,极易被泄露
  • 文件权限宽松:密钥文件可被 Web 服务器以外用户读取

✅ 安全最佳实践

1. 使用环境变量 + .env(开发/测试)

借助 vlucas/phpdotenv 加载环境变量,.env 文件绝不提交至 Git

# .env(添加到 .gitignore)
APP_ENCRYPTION_KEY=base64:YzVjZjMwYjE5ZjQwZjIyNjUxNmY1ZDQwYzJiMmYxYjE0NjYyNjY0YzRkNzA5MjYzYjUwYjEwYjQxZTQxZjQxZQ==

加载并验证密钥:

<?php
require_once 'vendor/autoload.php';
Dotenv\Dotenv::createImmutable(__DIR__)->load();

$key = $_ENV['APP_ENCRYPTION_KEY'] ?? null;
if (!$key || !str_starts_with($key, 'base64:')) {
    throw new RuntimeException('Invalid or missing encryption key');
}

// 解码为二进制密钥(适用于 Sodium 或 OpenSSL)
$rawKey = base64_decode(substr($key, 7));
?>

2. 生产环境:使用系统密钥管理服务

推荐使用 Linux 内核密钥保留服务(keyctl) 或云平台 KMS(如 AWS KMS、阿里云 KMS):

<?php
// 示例:从 Linux keyring 安全读取(需提前注入)
function getSecureKey(string $keyName): string {
    $output = shell_exec("keyctl show | grep -q '$keyName' && keyctl pipe $(keyctl search @u user '$keyName') 2>/dev/null");
    if (!$output) {
        throw new RuntimeException("Key '$keyName' not found in kernel keyring");
    }
    return $output;
}

try {
    $encryptionKey = getSecureKey('app_encryption_v1');
} catch (Exception $e) {
    error_log('Failed to load encryption key: ' . $e->getMessage());
    die('Service unavailable');
}
?>

3. 使用 Sodium 执行 AEAD 加密(推荐)

Sodium(PHP 7.2+ 内置)提供经过验证的安全原语:

<?php
// 使用密钥加密敏感数据(如用户令牌)
$nonce = random_bytes(SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBLEN);
$ciphertext = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt(
    $data,
    $associatedData = '',
    $nonce,
    $encryptionKey // 来自安全来源
);

// 存储:$nonce . $ciphertext(共 48 字节前缀 + 密文)
$stored = base64_encode($nonce . $ciphertext);
?>
⚠️ 重要提醒:永远不要自行实现加密逻辑;始终使用 libsodium 或 OpenSSL 的高级封装(如 sodium_crypto_secretbox());定期轮换密钥并设计密钥版本标识。

安全不是功能,而是持续的过程。从今天起,把密钥当作最高机密——它不是配置项,而是应用的生命线。

```