最稳妥方案是使用openssl_encrypt配合aes-256-gcm(首选)或aes-256-cbc+hmac组合,必须每次随机生成iv、密钥需32字节且安全派生,密文与iv须base64编码传输,并严格校验hmac或gcm认证标签防篡改。

PHP接口用 openssl_encrypt 加密最稳妥
直接上结论:别用 mcrypt(已废弃),也别手写 XOR 或 Base64 当加密——它们不防篡改、不抗重放。PHP 7.1+ 唯一推荐是 openssl_encrypt 配合 AES-256-CBC 或 AES-256-GCM。
关键不是“能不能加”,而是“加完别人解不开、改不了、重放无效”。所以必须带 IV(初始化向量)+ HMAC 签名(或直接用 GCM 模式)。GCM 更好,但部分旧环境不支持,先按 CBC + HMAC 组合讲:
-
$cipher = 'AES-256-CBC':必须固定,不能用 ECB(不安全)、不能用短密钥 - IV 必须每次随机生成(
random_bytes(16)),且随密文一起传,但绝不复用 - 密钥建议从
hash_hmac('sha256', $password, $salt, true)衍生,别硬编码字符串 - 加密后,对密文 + IV 做一次
hash_hmac('sha256', $ciphertext.$iv, $hmac_key),拼在末尾防篡改
解密时 openssl_decrypt 失败的常见原因
90% 的“解不出”不是算法问题,而是参数错位或数据损坏。重点检查这几点:
- IV 长度必须严格匹配:AES 是 16 字节,
strlen($iv) !== 16直接失败 - 密文是否被 URL 编码/JSON 转义过?传参时用
base64_encode()包一层,接收端先base64_decode() - 密钥长度不对:
AES-256要 32 字节密钥,mb_strlen($key, '8bit') !== 32会静默返回false - 没校验 HMAC 就急着解密——攻击者改一个字节,
openssl_decrypt可能返回乱码而非false,必须先验签
接口传输中怎么安全传密文和 IV
别把密文塞进 URL 参数(长度限制、日志泄露)、也别明文放 JSON key 里。统一走 POST body,并约定结构:
{"data": "base64_encoded_ciphertext", "iv": "base64_encoded_iv", "hmac": "hex_hmac_hash"}
服务端收到后:
- 先
base64_decode()$data和$iv,检查是否false(说明编码损坏) - 用相同密钥重新计算
hash_hmac('sha256', $data.$iv, $hmac_key),跟传来的$hmac严格比对(用hash_equals(),防时序攻击) - 只有验签通过,才调用
openssl_decrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv)
为什么不用 JWT 或现成 SDK
JWT 默认不加密(只签名),要加密得用 JWE,但 PHP 的 firebase/php-jwt 对 JWE 支持弱、文档少、容易配错算法。而原生 openssl_* 函数可控性强,出问题能定位到哪一步——比如 openssl_error_string() 能立刻告诉你 IV 长度错还是密钥错。
真正容易被忽略的是时间戳和重放:加密本身不防重放。必须在业务层加 timestamp 字段,服务端检查是否超 300 秒,且缓存近期 hmac 值做去重。这点再强的加密库也不会帮你做。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











