phpseclib 和 defuse/php-encryption 是 php 中真正可落地的加密封装方案,但需严格对齐密钥、格式与环境;defuse 密文为二进制且含版本头、nonce 和 auth tag,未 base64 编码会导致截断或乱码;phpseclib 应混合使用 rsa 加密 aes 密钥 + aes-gcm 加密数据,并显式启用认证模式;加密过滤层应置于 eloquent mutator 而非中间件;缺 openssl 或 sodium 扩展会导致静默降级为不兼容格式。

phpseclib 和 defuse/php-encryption 是目前 PHP 中真正能落地的加密封装方案,但直接“封装成一层”容易踩坑——不是代码写不对,而是密钥、格式、环境三者没对齐。
为什么不能直接用 Crypto::encrypt() 封装成通用过滤层?
Defuse 库生成的密文是 binary,含版本头(1 byte)、nonce(12 byte)、ciphertext + auth tag(AES-GCM),共约 48+ 字节。一旦你把它塞进 JSON 字段、MySQL TEXT 列或日志输出里,没做 base64_encode(),就会被截断或乱码,解密时必然抛出 Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException。
常见错误现象:
- 本地测试 OK,上线后批量解密失败,报错但密钥没动、代码没改
- 数据库字段类型设为
VARCHAR(255),而 base64 密文实际长度超 300 字符 - 用
json_encode()包裹加密结果,导致二进制被转义成 \uXXXX 形式,彻底不可逆
phpseclib 的 Crypt_AES 和 Crypt_RSA 怎么组合才安全?
纯 AES 适合加密大量数据(如用户地址、备注),但密钥必须安全分发;纯 RSA 只能加密很短的内容(≤245 字节 for 2048-bit key),不适合直接加密字段。混合用才是正解:
- 用
Crypt_RSA加密一个随机生成的 AES 密钥(Crypt_Random::string(32)) - 用该 AES 密钥 +
Crypt_AES(mode:gcm)加密原始数据 - 把 RSA 加密后的 AES 密钥 + AES 加密后的密文 + IV + auth tag 拼成结构化字符串(建议 JSON 或自定义分隔)
- 解密时先用私钥解出 AES 密钥,再用它解密主体数据
注意:Crypt_AES 默认不带认证,必须显式启用 GCM 模式并校验 tag,否则无法防御篡改。
敏感信息过滤层该在哪一环介入?
不是在 Controller 输入时就加密,也不是在 Model 保存前硬编码调用 —— 过滤层应绑定到 Eloquent 的 set mutator 和 get mutator,或 Doctrine 的 lifecycle callback。这样能保证:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 所有读写都经过同一逻辑路径,避免漏掉某个 API 入口
- 密钥可从环境变量注入(如
$_ENV['ENCRYPTION_KEY']),不随模型硬编码 - 字段级控制:只对
email、id_number等字段启用,其他字段透明透传
别在中间件里做这事 —— 中间件无法区分是创建还是更新,也无法感知字段是否已被加密过,极易重复加密或解密失败。
Composer 安装后,哪些扩展缺失会导致静默降级?
defuse/php-encryption 在 PHP 7.2+ 下依赖 openssl 和 sodium。缺任一,它会自动 fallback 到 AES-CBC + HMAC 格式,但该格式与标准 AES-GCM 不兼容,跨环境(如 dev → prod)就会解密失败。
验证方式很简单:
- 运行
php -m | grep -E '^(openssl|sodium)$' - 检查
vendor/defuse/php-encryption/lib/Crypto.php中isAvailable()返回值 - CI 流程里加一行
php -r "echo extension_loaded('sodium') ? 'ok' : 'fail';"
最易被忽略的是:某些 Docker 镜像(如 php:8.2-cli)默认不装 sodium,得手动 docker-php-ext-install sodium。










