不能靠composer require一装就完事——它只提供aes-gcm安全基元,密钥加载错误、密文未base64编解码、openssl或sodium扩展缺失任一问题,均会导致上线后批量抛wrongkeyormodifiedciphertextexception。

不能靠 composer require defuse/php-encryption 一装就完事——它只给你一个安全的加密基元,而密钥加载方式错、密文存取不一致、PHP扩展缺失,三者任一出问题,上线后就会批量抛 Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException,且几乎无法回溯。
确认 PHP 环境是否真正支持 Defuse
Defuse v3.0.0 要求 openssl 和 sodium 两个扩展同时启用,缺一不可。它不会报错退出,而是静默降级到 AES-CBC + HMAC 模式,生成的密文格式与 AES-GCM 不兼容,导致跨环境加解密失败。
- 运行
php -m | grep -E "(openssl|sodium)",必须看到两行输出;若无sodium,Ubuntu/Debian 执行sudo apt install php-sodium,macOS(PHP 8.2+)用brew install php@8.2 - 检查
php.ini中是否启用了extension=openssl和extension=sodium(部分系统需取消注释) - 别信
paragonie/sodium_compat能兜底——Defuse 不走它的兼容层,它只对直接调用sodium_*函数生效
安装时必须匹配 PHP 版本和稳定性策略
v3.x 仅支持 PHP ≥ 7.2,但实际踩坑点在于 Composer 的稳定性锁和镜像源。很多项目卡在 Your requirements could not be resolved,不是包名写错,是版本策略冲突。
- 先查 PHP 版本:
php -v;PHP 7.1 只能用defuse/php-encryption:^2.2.1,PHP 8.x 必须用^3.0.0 - 删掉
composer.json里"minimum-stability": "stable"这行——它会阻止 v3.0.0 安装,因某些镜像尚未同步其 stable 标签 - 换国内镜像源:
composer config -g repo.packagist composer https://mirrors.cloud.tencent.com/composer/,再执行composer require defuse/php-encryption:^3.0.0 - 装完立刻删掉
vendor/和composer.lock,重装一次——旧项目常因残留 v1/v2 类导致Fatal error: Cannot declare class Defuse\Crypto\Key
密钥不能硬编码,也不能用 bin2hex 存储
Defuse 的密钥是对象,不是字符串。用 Crypto::generateKey() 生成的是 Defuse\Crypto\Key 实例,必须用 Key::loadFromAsciiSafeString() 加载,任何手动 hex/base64 编解码都会破坏其内部结构。
- 生成密钥:新建
generate-key.php,内容为echo \Defuse\Crypto\Key::createNewRandomKey()->saveToAsciiSafeString();,运行后得到一串 ASCII 字符串 - 保存路径建议为
/etc/app-defuse-key.txt(Linux)或runtime/keys/encryption.key(ThinkPHP),权限设为600 - 加载时必须用
$key = \Defuse\Crypto\Key::loadFromAsciiSafeString(file_get_contents('/path/to/key.txt'));,绝不能bin2hex(file_get_contents(...))或拼接进配置数组 - 别把密钥塞进
.env——ASCII 安全字符串含换行和特殊字符,dotenv 解析易截断
密文入库前必须 base64_encode,读取后必须 base64_decode
Defuse 输出的密文是二进制数据,含版本头(1 字节)、nonce(24 字节)、密文主体、认证标签(16 字节)。直接存 MySQL TEXT 或 JSON 字段会丢失字节,尤其开头的 0x01 版本标识。
- 加密后立即
$encoded = base64_encode($ciphertext),存入数据库字段(类型建议TEXT或VARCHAR(512)) - 读库后立刻
$ciphertext = base64_decode($encoded),再传给Crypto::decrypt() - 禁止用
json_encode()包一层——它会把二进制转成乱码字符串,json_decode()后已不可逆 - 别在控制器里手动拼接密文和 IV,Defuse 自带 nonce 和认证,不需要你操心
最常被忽略的是:密文长度不是固定值,它随明文长度线性增长,且每次加密结果都不同(因为 nonce 随机)。如果你在测试时用相同输入反复加密,却拿第一次的结果去解第二次的密文,就会触发 WrongKeyOrModifiedCiphertextException——这不是密钥错了,是根本没传对密文。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











