ThinkPHP接口加密必须在中间件中拦截请求/响应流:响应加密需在response事件中读取原始内容、AES加密后替换响应体并重设Content-Type和Content-Length;请求解密须在中间件中检查加密标识、解密后通过$request->merge()注入数据,确保控制器正常获取;IV须每次随机生成且密钥严禁硬编码。

ThinkPHP 接口加密不能只靠 encrypt() 函数包一层就完事——它默认只做本地加解密,不处理 HTTP 请求/响应流,直接用于 API 传输会丢状态码、破坏 Header、让前端收不到 JSON 解析结果。
中间件里改 $response->content() 才是正解
响应体加密必须在框架输出前拦截原始内容,而不是等控制器 return 后再加工。否则 JsonResponse 中间件已把数据转成 JSON 并设好 Content-Type: application/json,你再套壳就全乱了。
- 中间件必须注册在
think\middleware\JsonResponse之前(比如通过app/middleware.php数组键名控制顺序,或显式设priority) - 用
$response->getContent()读原始字符串,AES 加密后调用$response->content($encrypted)替换 - 手动重设
Content-Type(如application/octet-stream或自定义application/vnd.api+encrypted),并更新Content-Length - 别用
json_encode()再包一次——这会让前端收到双层 JSON,解析失败
$_POST 拿不到解密数据?因为没在中间件里重写输入流
ThinkPHP 的 $request->post() 和 input() 都基于原始 php://input 解析。如果请求体是 base64 + AES 密文,框架直接当无效 JSON 或空数组处理。
- 必须在中间件中检查请求头(如
X-Encrypted: aes-128-cbc)或路径前缀(如/api/v2/secure/)来触发解密 - 解密后不要只存变量,要用
$request->merge(['post' => $decrypted])注入,后续所有控制器都能正常调用$this->request->post() - 避免用
file_put_contents('php://input', ...)重写流——PHP 不允许写入该流,会静默失败 - 别在
AppInit阶段做这事:此时$request对象还没完成初始化,merge()会报错
AES 的 IV 和密钥怎么传才不翻车
IV 必须每次请求随机生成并随密文一起发给服务端;密钥绝不能出现在 JS、HTML 或接口响应里——这是最常被忽略的安全断点。
立即学习“PHP免费学习笔记(深入)”;
- IV 长度由算法决定(如 AES-128-CBC 是 16 字节),用
openssl_random_pseudo_bytes(16)生成,拼在密文前或 Base64 后一起传输 - 密钥只能从服务端环境变量读取(如
env('CRYPTO_KEY')),严禁硬编码在 PHP 文件或 JS 里 - 前端加密时若用 Web Crypto API,注意 Chrome/Firefox 对
SubtleCrypto的 IV 处理和 PHPopenssl_encrypt兼容性:确保都用Uint8Array转字节,别用字符串直传 - 测试阶段可用固定 IV 方便调试,但上线前必须切回随机 IV,否则重放攻击风险极高
真正难的不是写加密函数,而是让加密逻辑和 ThinkPHP 的生命周期对齐——响应要早于 JSON 处理,请求要早于 input 解析,密钥要隔离于代码,IV 要绑定单次请求。漏掉任一环,加密就等于没加。



















