不可逆的哈希算法(如 password_hash()
),而需要加密解密的场景应使用对称加密算法。以下是两种情况的实现示例:
一、密码哈希存储(推荐方案)
适用于用户登录验证场景,无法解密还原明文密码。
<?php
// 密码哈希存储示例
$password = 'user123'; // 用户输入的原始密码
// 生成安全哈希(自动包含盐值)
$hash = password_hash($password, PASSWORD_DEFAULT);
// 验证密码(返回 true/false)
$isValid = password_verify($password, $hash);
echo "原始密码:$password\n";
echo "存储哈希:$hash\n";
echo "验证结果:" . ($isValid ? '通过' : '失败');
输出示例:
原始密码:user123
存储哈希:$2y$10$5sN2uY3JvE8k7KbXo1Qz3O9mW6cRfGhTlLpAqSrDtUvCwIjMn.ZG
验证结果:通过
二、可逆加密方案(需谨慎使用)
使用 AES-256-CBC 对称加密算法,适用于需要还原明文的场景。
<?php
class SecureCrypto {
const METHOD = 'aes-256-cbc';
const KEY_LENGTH = 32; // 256位密钥
/**
* 生成加密密钥(应安全存储)
*/
public static function generateKey() {
return openssl_random_pseudo_bytes(self::KEY_LENGTH);
}
/**
* 加密数据
*/
public static function encrypt($plaintext, $key) {
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(self::METHOD));
$ciphertext = openssl_encrypt(
$plaintext,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
return base64_encode($iv . $ciphertext);
}
/**
* 解密数据
*/
public static function decrypt($data, $key) {
$data = base64_decode($data);
$iv = substr($data, 0, openssl_cipher_iv_length(self::METHOD));
$ciphertext = substr($data, openssl_cipher_iv_length(self::METHOD));
return openssl_decrypt(
$ciphertext,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
}
}
// ----------------- 使用示例 -----------------
$secretKey = SecureCrypto::generateKey(); // 生成并保存密钥
$password = 'user123';
// 加密
$encrypted = SecureCrypto::encrypt($password, $secretKey);
echo "加密后:$encrypted\n";
// 解密
$decrypted = SecureCrypto::decrypt($encrypted, $secretKey);
echo "解密后:$decrypted\n";
输出示例:
加密后:8vY6V3TZ4j...(Base64编码字符串)
解密后:user123
关键安全注意事项
哈希存储最佳实践
永远使用
password_hash()
+password_verify()
不需要(也无法)解密原始密码
自动处理盐值和算法升级
加密方案注意事项
密钥管理:密钥必须安全存储(不能硬编码在代码中)
IV随机性:每次加密必须使用不同的初始化向量
传输安全:加密数据仍需通过 HTTPS 传输
算法选择:使用 AES-256 等现代算法
典型应用场景对比
场景 哈希存储 加密方案 用户登录验证 ✅ 推荐使用 ❌ 不适用 支付信息存储 ❌ 无法使用 ✅ 需要解密时使用 密码找回功能 ❌ 无法实现 ⚠️ 存在安全隐患
三、综合建议
用户密码 必须使用哈希方案
需要解密的敏感数据(如信用卡号)应:
使用经过审计的加密库
将密钥存储在硬件安全模块(HSM)中
定期轮换加密密钥
实际开发中应优先考虑使用成熟的加密库(如 libsodium):
// Libsodium 加密示例(PHP 7.2+)
$key = sodium_crypto_secretbox_keygen();
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$ciphertext = sodium_crypto_secretbox('secret data', $nonce, $key);
$plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);