PHP 加密安全常见漏洞与防护
加密是 Web 应用安全的基石,但错误使用 PHP 加密函数极易引入严重风险。以下是开发者最常踩的三大陷阱及正确实践。
❌ 漏洞一:使用过时/不安全的哈希算法
md5() 和 sha1() 已被证实易受碰撞攻击,且无盐值保护,无法抵御彩虹表攻击。
⚠️ 危险示例:
// ❌ 绝对禁止!
$password_hash = md5($_POST['password']); // 无盐、可逆性弱、已破解
✅ 正确做法:使用 PHP 内置的 password_hash()(基于 bcrypt)并配合 password_verify():
// ✅ 安全哈希(自动加盐 + 自适应成本)
$hash = password_hash($_POST['password'], PASSWORD_ARGON2ID); // 或 PASSWORD_BCRYPT
// 验证时:
if (password_verify($input, $stored_hash)) {
echo "登录成功";
}
❌ 漏洞二:手动实现加密逻辑(如自定义 AES)
绕过 OpenSSL 手写加解密极易出错——缺少认证加密(AEAD)、IV 重用、硬编码密钥等都会导致数据泄露。
⚠️ 危险示例:
// ❌ 错误:ECB 模式(不安全)、无认证、密钥明文
$cipher = openssl_encrypt($data, 'AES-128-ECB', 'mykey123', 0);
✅ 正确做法:使用 openssl_encrypt() 的 GCM 模式(提供机密性+完整性):
// ✅ 安全 AES-GCM(需 PHP 7.1+)
$key = random_bytes(32); // 256-bit 密钥(应安全存储)
$iv = random_bytes(12); // GCM 推荐 96-bit IV
$ciphertext = openssl_encrypt(
$data,
'aes-256-gcm',
$key,
OPENSSL_RAW_DATA,
$iv,
$tag, // 认证标签(必须保存)
'',
16 // AEAD 标签长度
);
// 存储:$iv . $tag . $ciphertext(解密时需全部还原)
❌ 漏洞三:硬编码密钥或密钥管理失当
将密钥写死在代码中、存于 Git 仓库或配置文件,等于主动交出保险柜钥匙。
✅ 正确做法:密钥分离存储 + 环境隔离:
// ✅ 使用环境变量(.env 文件不提交 Git)
$key = $_ENV['APP_ENCRYPTION_KEY'] ?? null;
if (!$key || strlen($key) < 32) {
throw new RuntimeException("无效加密密钥");
}
// 推荐:使用 sodium_crypto_secretbox()(PHP 7.2+,更简洁安全)
$nonce = randombytes_buf(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$encrypted = sodium_crypto_secretbox($data, $nonce, $key);
📌 最佳实践总结
- 密码哈希:只用
password_hash()/password_verify() - 对称加密:优先选
sodium_crypto_secretbox()或openssl_encrypt('aes-256-gcm') - 密钥管理:通过环境变量注入,禁用硬编码;生产环境使用密钥管理服务(如 AWS KMS)
- 定期审计:使用 PHPStan 或 Psalm 检测危险函数调用
安全不是功能,而是贯穿开发生命周期的习惯。每一次加密操作,都请自问:它是否经得起专业攻击者的审视?
```