PHP用openssl_encrypt加密必须正确设置模式和填充,默认PKCS#7填充最安全;密钥需严格16或32字节,IV须随机生成并存储;MySQL字段应选VARBINARY存二进制密文,避免字符集转换。

PHP 用 openssl_encrypt 加密字段前必须设对模式和填充
直接调 openssl_encrypt 却解不出,八成是 OPENSSL_ZERO_PADDING 或 OPENSSL_PKCS1_PADDING 搞错了。AES 是分组加密,PHP 默认用 PKCS#7 填充(openssl_encrypt 内部自动处理),但如果你手动用了 OPENSSL_ZERO_PADDING,就得自己补零、对齐 16 字节,否则解密失败或乱码。
常见错误现象:openssl_decrypt 返回 false,或解出乱码但长度刚好是 16 的倍数。
- 生产环境一律用
OPENSSL_ENCRYPT+OPENSSL_ZERO_PADDING配合手动填充——不推荐,容易出错 - 默认就用
OPENSSL_ENCRYPT(不用显式传 padding 参数),让 PHP 自动处理 PKCS#7 - 密钥长度必须严格:AES-128 要 16 字节,AES-256 要 32 字节;用
hash('sha256', $key, true)安全截取,别用substr截字符串 - IV 必须每次加密随机生成,且和密文一起存(比如拼在密文前,或另存一列),不能复用、不能硬编码
MySQL 里存 AES 加密数据,字段类型选 VARBINARY 不是 VARCHAR
加密后的二进制数据不是 UTF-8 文本,用 VARCHAR 存会触发字符集转换(比如从 utf8mb4 转成 latin1),再读出来就废了。哪怕你 Base64 编码后存字符串,也得确认连接字符集和字段校对规则不干扰 —— 最省心是存原生二进制。
使用场景:用户身份证号、手机号、银行卡号等敏感列,要求落库即密文,应用层透明加解密。
- 字段类型必须是
VARBINARY(255)(AES-256-CBC 下,100 字符原文加密后约 136 字节,留点余量) - 如果非要存 Base64 字符串,字段用
VARCHAR(344)(136×2 四舍五入),并确保 PDO 连接时加;charset=binary或显式设PDO::ATTR_EMULATE_PREPARES => false - 不要用 MySQL 自带的
AES_ENCRYPT()—— 密钥管理难、无法跨语言、且 8.0.30+ 已标记为 deprecated
解密失败时先查 openssl_error_string(),不是重写逻辑
PHP 的 OpenSSL 扩展报错极其安静,openssl_decrypt 失败只返回 false,不抛异常。你改十遍 IV 生成方式,不如花 10 秒看下 OpenSSL 底层到底卡在哪。
性能影响:每次加解密都调 openssl_error_string() 有开销,但开发期必须开;上线后可关,靠日志捕获 false 后再临时启用排查。
- 在解密后立刻加:
if ($decrypted === false) { error_log('OpenSSL error: ' . openssl_error_string()); } - 典型错误:
error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt→ IV 或密钥错、或密文被截断/损坏 -
error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length→ 填充方式不匹配,比如加密用默认 PKCS#7,解密却传了OPENSSL_ZERO_PADDING
Laravel Eloquent 中透明加密列,别碰 casts 里的 encrypted
Laravel 9+ 内置的 encrypted cast 看似方便,但它用的是 OpenSSL + base64 + 固定 IV(其实是用 key 衍生的),密钥轮换困难,且不支持自定义 IV 存储位置。一旦数据库迁移或服务拆分,基本没法平滑过渡。
真实使用场景:老项目要快速给几个字段加密,又不想大改 ORM 层。
- 自己写一个
EncryptedCast类,实现get/set,把 IV 存进同一行另一列(如phone_iv),密文存phone_enc - 避免在
set里直接调parent::set,否则会触发两次序列化;原始值应走$model->attributes['phone_enc'] = $ciphertext - 不要在模型事件(如
saving)里统一加密 —— 查询时不会自动解,还得额外加访问器,容易漏
最麻烦的从来不是加密本身,而是密钥怎么安全注入、IV 怎么跟密文原子写入、以及——当某天要换密钥重加密全表时,那段跑在凌晨三点的 migration 脚本,有没有加锁和断点续传。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











