本文目录导读:

在 PHP 中进行认证加密,最核心的原则是:永远不要自己实现加密算法,而是使用经过安全审计的官方扩展。
最推荐、最现代、最安全的方式是使用 Sodium 扩展(PHP 7.2+ 自带,现代标准)或 OpenSSL 扩展(传统标准)。
以下是 PHP 认证加密的三种主要实现方式,按推荐程度排序:
关键概念解释
- 认证加密:不仅加密数据(保证机密性),还使用 MAC(消息认证码)验证数据的完整性和真实性(确保数据未被篡改)。
- 你需要的参数:
plaintext:要加密的原文。password/key:你的密钥。永远不要将用户密码直接作为 key 使用,必须经过 KDF(密钥派生函数)处理。nonce/IV:每次加密必须唯一的随机数。不要重复使用。
推荐方式:使用 libsodium(Sodium)
这是 PHP 7.2 之后的内置扩展,代表了现代加密的标准,它简单、安全、难以误用。
函数:sodium_crypto_secretbox(对称加密)
<?php
// 1. 生成一个可靠的密钥(只需执行一次,存储起来)
// 需要从安全随机源生成
$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); // 32字节
// 2. 加密
function encryptData(string $plaintext, string $key): string {
// 生成一个 Nonce(只使用一次的数字),必须是唯一的
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); // 24字节
// 加密并认证(返回的是原始二进制数据)
$ciphertext = sodium_crypto_secretbox($plaintext, $nonce, $key);
// 安全擦除内存中的明文
sodium_memzero($plaintext);
// 将 Nonce 与密文拼接传输(因为解密时需要 Nonce)
return base64_encode($nonce . $ciphertext);
}
// 3. 解密
function decryptData(string $encryptedBase64, string $key): string|false {
$decoded = base64_decode($encryptedBase64);
if ($decoded === false || strlen($decoded) < SODIUM_CRYPTO_SECRETBOX_NONCEBYTES) {
return false; // 数据被篡改或无效
}
// 拆分 Nonce 和密文
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
// 解密并验证,如果数据被篡改,立即返回 false(这是核心优势)
$plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
if ($plaintext === false) {
// 认证失败!数据被篡改或密钥错误
return false;
}
sodium_memzero($ciphertext);
return $plaintext;
}
// ---- 使用示例 ----
$myMessage = "这是绝密信息: 密码是苹果";
$encrypted = encryptData($myMessage, $key);
echo "加密结果: " . $encrypted . PHP_EOL;
$decrypted = decryptData($encrypted, $key);
echo "解密结果: " . $decrypted . PHP_EOL;
// 试试篡改数据:
$tampered = substr_replace($encrypted, 'X', 5, 1); // 修改第6个字符
$result = decryptData($tampered, $key);
var_dump($result); // 输出: bool(false)
?>
优点:
- 防误用:自动使用了
XSalsa20加密(流加密)+Poly1305消息认证码(高效、安全)。 - 如果数据被篡改,
sodium_crypto_secretbox_open直接返回false,逻辑很干净。 - 官方库,性能好。
经典方案:使用 OpenSSL 实现认证加密
如果你必须支持 PHP 5.6+ 且无法使用 Sodium,可以使用 OpenSSL 的 AEAD 模式(AES-256-GCM)。
函数:openssl_encrypt 和 openssl_decrypt
<?php
// 1. 生成密钥
$key = random_bytes(32); // AES-256 需要 32 字节
// 2. 加密
function encryptOpenSSL(string $plaintext, string $key): string {
$cipher = "aes-256-gcm"; // 推荐使用 GCM 模式(认证加密)
$ivlen = openssl_cipher_iv_length($cipher);
$iv = random_bytes($ivlen); // GCM 的 IV 通常为 12 字节
// 标签变量会通过引用填充认证码
$tag = '';
$ciphertext = openssl_encrypt(
$plaintext,
$cipher,
$key,
OPENSSL_RAW_DATA,
$iv,
$tag,
"", // aad:额外的认证数据(可选)
16 // tag长度
);
// 检查是否失败
if ($ciphertext === false) {
throw new Exception('加密失败');
}
// 返回 base64 编码的 [IV + 密文 + 认证标签]
return base64_encode($iv . $ciphertext . $tag);
}
// 3. 解密
function decryptOpenSSL(string $encryptedBase64, string $key): string|false {
$cipher = "aes-256-gcm";
$ivlen = openssl_cipher_iv_length($cipher);
$taglen = 16; // 与加密时对应
$decoded = base64_decode($encryptedBase64);
// 提取 IV、密文、标签
$iv = substr($decoded, 0, $ivlen);
$ciphertext = substr($decoded, $ivlen, -$taglen);
$tag = substr($decoded, -$taglen);
$plaintext = openssl_decrypt(
$ciphertext,
$cipher,
$key,
OPENSSL_RAW_DATA,
$iv,
$tag
);
// 如果认证失败,返回 false
if ($plaintext === false) {
return false;
}
// 安全清理不是必须的,但养成习惯
sodium_memzero($ciphertext); // 注意:这里是假设你加载了 sodium,如果不支持,可以忽略这行
return $plaintext;
}
// ---- 使用示例 ----
$data = "我的秘密配置:API_KEY=xxx";
$enc = encryptOpenSSL($data, $key);
echo "加密: " . $enc . PHP_EOL;
$dec = decryptOpenSSL($enc, $key);
echo "解密: " . $dec . PHP_EOL;
// 篡改测试
$enc2 = substr_replace($enc, 'Z', 10, 1);
echo "篡改结果: ";
var_dump(decryptOpenSSL($enc2, $key)); // false
?>
注意:GCM 的 nonce(IV)和 tag 必须严格分离并存储,openssl_decrypt 会自行验证。
操作建议:如何处理“密码”输入
用户通常输入类似 my_password123 的密码,但不能直接用做加密密钥。
必须使用 KDF(密钥派生函数):
// 用户输入的密码必须是强密码或使用盐
$userPassword = "ilovePHP";
$salt = random_bytes(SODIUM_CRYPTO_PWHASH_SALTBYTES); // 16 字节,保存好
// 使用 Sodium 的 KDF 将密码转为 32 字节 key
$key = sodium_crypto_pwhash(
32, // 输出密钥长度
$userPassword,
$salt,
SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, // 计算强度
SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);
或者使用 hash_pbkdf2(OpenSSL 兼容):
$salt = random_bytes(16);
$key = hash_pbkdf2("sha256", $userPassword, $salt, 100000, 32, true);
应该用哪个?
| 场景 | 推荐方案 |
|---|---|
| PHP 7.2+ 标准环境 | libsodium — 最简单、最安全、最难出错。 |
| PHP 5.6+,需要兼容旧系统 | OpenSSL + AES-256-GCM — 标准通用,但需要自己管理 nonce 和 tag。 |
| 存储密码(哈希,不是加密) | password_hash + password_verify — 永远不要加密密码,要哈希。 |
最终结论:如果你刚开始写代码,学会 sodium_crypto_secretbox 对你的数据保护帮助最大,它能保证你在不仔细思考细节的情况下也不容易犯安全错误。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播