PHP 对称加密算法选型

wen PHP项目 2

本文目录导读:

PHP 对称加密算法选型

  1. 核心结论(选型总结)
  2. 绝对首选:AES-256-GCM(推荐)
  3. 次选方案:AES-256-CBC + HMAC(兼容旧环境)
  4. 密钥管理(比算法更重要)
  5. 防坑指南(注意事项)
  6. 终极替代方案:现代加密库(libsodium)
  7. 最终决策树

在 PHP 中进行对称加密,首选方案是 openssl_encrypt / openssl_decrypt,结合 AES-256-GCM 模式

以下是详细的选型指南、代码示例及安全注意事项。


核心结论(选型总结)

算法/模式 推荐度 说明
AES-256-GCM ⭐⭐⭐⭐⭐ 首选,提供认证加密(AEAD),防止密文被篡改,性能优秀,PHP 7.1+ 原生支持。
AES-256-CBC + HMAC-SHA256 ⭐⭐⭐⭐ 次选,若环境不支持 GCM(如老版本),用于在不支持 AEAD 时实现"加密+签名"。
AES-256-CTR ⭐⭐⭐ 流模式,无填充,但必须配合 HMAC,否则不安全。
mcrypt 弃用 PHP 7.1 已废弃,7.2 移除,严禁使用
Sodium ⭐⭐⭐⭐⭐ PHP 7.2+ 内置,现代加密库(libsodium),但 API 相对底层。

绝对首选:AES-256-GCM(推荐)

GCM 模式在加密的同时自动生成认证标签(Tag),解密时若不提供正确的 TAG 会直接失败,防止"填充预言攻击"。

示例代码(PHP 7.1+)

<?php
/**
 * AES-256-GCM 加密
 * @param string $plaintext 明文
 * @param string $key 密钥(必须是 32 字节,建议用 hash('sha256', $password, true) 派生)
 * @return array [密文, IV, TAG]
 */
function aes_gcm_encrypt(string $plaintext, string $key): array {
    // 生成随机 IV(推荐 12 字节,GCM 标准长度)
    $iv = random_bytes(12);
    // 加密并生成认证标签(TAG)
    $ciphertext = openssl_encrypt(
        $plaintext,
        'aes-256-gcm',
        $key,
        OPENSSL_RAW_DATA, // 不进行 Base64 编码,直接返回原始二进制
        $iv,
        $tag // 16 字节的认证标签
    );
    return [$ciphertext, $iv, $tag];
}
/**
 * AES-256-GCM 解密
 * @param string $ciphertext 密文
 * @param string $iv IV
 * @param string $tag 认证标签
 * @param string $key 密钥
 * @return string|false 明文,验证失败返回 false
 */
function aes_gcm_decrypt(string $ciphertext, string $iv, string $tag, string $key) {
    $plaintext = openssl_decrypt(
        $ciphertext,
        'aes-256-gcm',
        $key,
        OPENSSL_RAW_DATA,
        $iv,
        $tag
    );
    return $plaintext;
}
// --- 使用示例 ---
$key = hash('sha256', '很长的密码/密钥', true); // 确保 32 字节密钥
[$ciphertext, $iv, $tag] = aes_gcm_encrypt('这是要加密的机密数据', $key);
// 存储时建议将三者合并:base64_encode($iv . $tag . $ciphertext)
$combined = base64_encode($iv . $tag . $ciphertext);
echo "加密后: " . $combined . PHP_EOL;
// 解密
$decoded = base64_decode($combined);
$iv = substr($decoded, 0, 12);
$tag = substr($decoded, 12, 16);
$ciphertext = substr($decoded, 28);
$plaintext = aes_gcm_decrypt($ciphertext, $iv, $tag, $key);
var_dump($plaintext); // 输出明文
?>

次选方案:AES-256-CBC + HMAC(兼容旧环境)

如果目标环境 PHP < 7.1(几乎绝迹),或需要兼容特殊库,使用 CBC 模式,但必须对密文进行 HMAC 完整性校验(加密后再签名)。

关键点:先加密(CBC),再计算 MAC(HMAC);解密时先验证 MAC,再解密。

<?php
function cbc_encrypt($plaintext, $key) {
    $iv = random_bytes(16); // CBC 需要 16 字节 IV
    $ciphertext_raw = openssl_encrypt(
        $plaintext,
        'aes-256-cbc',
        $key,
        OPENSSL_RAW_DATA,
        $iv
    );
    // 生成 HMAC(签名密文 + IV)
    $hmac = hash_hmac('sha256', $iv . $ciphertext_raw, $key, true);
    // 合并并返回
    return base64_encode($iv . $hmac . $ciphertext_raw);
}
function cbc_decrypt($data, $key) {
    $decoded = base64_decode($data);
    $iv = substr($decoded, 0, 16);
    $hmac = substr($decoded, 16, 32);
    $ciphertext = substr($decoded, 48);
    // 验证 HMAC(防止篡改)
    $calc_hmac = hash_hmac('sha256', $iv . $ciphertext, $key, true);
    if (!hash_equals($hmac, $calc_hmac)) {
        return false; // 被篡改
    }
    return openssl_decrypt($ciphertext, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
}
?>

密钥管理(比算法更重要)

无论选哪种算法,密钥安全是第一位的:

要点 具体建议
不要硬编码 不要将密钥写在代码里,使用环境变量或专门的密钥管理服务(如 AWS KMS, Vault)。
使用二进制密钥 直接用 random_bytes(32) 生成,不要用字符串密码直接加密(因为熵不足),若要密码,用 password_hashhash_pbkdf2 派生。
密钥轮换 设计支持密钥版本号(如 k1:密文),定期更换密钥。
存储 将密钥文件权限设为 600,并确保 Web 服务器用户可读。

防坑指南(注意事项)

  1. IV 必须随机且唯一:永远不要重复使用同一个 IV + 同一个密钥,否则可通过 XOR 破解,使用 random_bytes() 生成。
  2. GCM 的 Tag 必须保存:解密时忘记传 Tag 或 Tag 错了,会导致解密失败或返回 false
  3. 不要用 ECB 模式:ECB 会泄露明文结构,绝对禁用
  4. 不要自己设计协议:除非你是密码学专家,否则使用标准库和标准模式。
  5. 编码问题openssl_encrypt 第四个参数为 OPENSSL_RAW_DATA 时返回二进制,存入数据库或 URL 传输前必须 base64_encode
  6. PHP 版本:确保 openssl 扩展已启用(php -m | grep openssl)。

终极替代方案:现代加密库(libsodium)

PHP 7.2+ 内置了 sodium 扩展,它提供更安全的 crypto_secretbox(基于 XSalsa20-Poly1305),API 更简单,且完全避免误用。

<?php
// libsodium (PHP 7.2+)
$key = sodium_crypto_secretbox_keygen(); // 生成 32 字节密钥
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$ciphertext = sodium_crypto_secretbox('机密数据', $nonce, $key);
// 解密
$plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);

如果你的 PHP 版本 ≥ 7.2,且不想处理 IV/TAG 拼接的细节,libsodium 是比 OpenSSL 更安全、更易用的选择。


最终决策树

你的 PHP 版本?
├── ≥ 8.0 → 如果安装 sodium 扩展 → 用 libsodium
│         → 否则用 OpenSSL AES-256-GCM ✅
├── 7.2 - 7.4 → 优先用 sodium,或 AES-256-GCM
├── 7.1 → AES-256-GCM(支持)
└── < 7.1 → 用 AES-256-CBC + HMAC(或升级 PHP,不建议在生产使用旧版)

一句话总结openssl_encrypt + aes-256-gcm,密钥用 random_bytes(32) 生成,IV 随机,Tag 妥善保存,一切走标准 API。

抱歉,评论功能暂时关闭!