PHP参数加密需兼顾可解密性、URL安全性与防篡改性:必须用openssl_encrypt配合AES-128-CBC等带随机IV的模式,加OPENSSL_RAW_DATA选项,密文经base64_encode后用strtr转URL安全字符,并强制添加hash_hmac('sha256')签名校验。

用openssl_encrypt做AES对称加密(推荐主流方案)
这是目前最通用、兼容性好、安全性达标的方案,尤其适合ThinkPHP等框架中加密GET参数或API响应体。
- 必须用
AES-128-CBC或AES-256-CBC这类带IV的块加密模式,别用ECB(不安全) - 密钥长度要匹配:AES-128需16字节,AES-256需32字节;建议用
hash_pbkdf2('sha256', $password, $salt, 100000, 32, true)派生,别直接用短口令 - IV必须每次随机生成(
random_bytes(16)),且随密文一起传输;不能固定、不能硬编码 - 调用时一定要加
OPENSSL_RAW_DATA选项,否则返回base64格式,还得额外做URL安全化 - 加密后密文是二进制,需转成URL安全Base64:先
base64_encode(),再用strtr($b64, '+/=', '-_.')替换特殊字符
加HMAC签名防篡改(关键一步)
光加密不够——攻击者可能截获密文后重放或调换参数。必须加入消息认证码(MAC)验证完整性。
- 服务端加密时,在密文后拼接一个HMAC校验值:
$hmac = hash_hmac('sha256', $ciphertext_urlsafe, $key, true) - 再把HMAC也做URL安全base64:
strtr(base64_encode($hmac), '+/=', '-_.') - 最终参数可传成
?token=xxx.yyy(密文.签名)或两个独立参数 - 解密前,先分离密文与签名,用相同密钥重新计算HMAC,严格比对——不一致就直接拒绝
- 别用
md5()或sha1()单独哈希,它们无密钥、易碰撞、不防重放
用Sodium扩展做现代加密(PHP 7.2+首选)
如果项目环境可控(如Laravel、新部署系统),sodium_crypto_secretbox是更优选择,自带认证、抗侧信道、nonce要求宽松。
- 密钥用
sodium_crypto_secretbox_keygen()生成,nonce用sodium_randombytes_buf(24)(XChaCha20-Poly1305要求) - 加密输出含认证标签,无需额外签名;解密失败直接返回
false,不抛异常 - nonce和密文一起存/传,同样要做URL安全base64转换(
strtr(base64_encode(), '+/=', '-_.')) - 注意:该方式不兼容旧系统,且PHP必须启用sodium扩展(7.2+默认开启)
避坑提醒:哪些做法要立刻停用
这些常见写法看似能“加密”,实则存在严重安全隐患,线上环境务必规避。
立即学习“PHP免费学习笔记(深入)”;
-
裸用
base64_encode():只是编码,毫无保密性和完整性保障 - 硬编码IV或复用IV:导致密文可预测,CBC模式下可能被填充预言攻击
-
用
mcrypt_*函数:PHP 7.1起废弃,7.2+已移除,漏洞多且无维护 -
MD5/SHA1做签名或密钥派生:碰撞风险高,应改用
hash_hmac('sha256', ...)或hash_pbkdf2 -
把密钥写死在代码里:应通过环境变量(
$_ENV['APP_KEY'])或配置中心管理



















