ThinkPHP中input()默认不防JSON注入,需禁用自动解析、手动限制深度并校验键名。应使用Request::post(false)获取原始body,显式声明可接收字段,过滤魔术方法名,预检JSON大小,并在多环节协同防御。

ThinkPHP里input()默认不防JSON注入
直接用input('param')或input()接收请求体里的JSON数据时,框架不会自动过滤、转义或校验结构——它只是把原始JSON字符串原样解析成PHP数组,然后交给你。这意味着如果前端传了恶意键名(比如__construct、__destruct)、非法嵌套、超大深度或含PHP魔术方法名的字段,后续若不经检查就进array_merge、extract、模型赋值或json_encode回写,就可能触发反序列化风险、内存溢出或逻辑绕过。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 禁用自动JSON解析:在
app.php配置中设'json_bind' => false,让框架不再对Content-Type: application/json请求自动调用json_decode - 手动解析必须加限制:用
json_decode($raw, true, 512, JSON_THROW_ON_ERROR),其中512是最大递归深度,防止栈溢出;JSON_THROW_ON_ERROR确保解析失败立刻抛异常,避免静默返回null - 别信任
input('param/a')这种带类型后缀的写法——它只做基础类型转换,不做结构校验,仍可能放行危险键名
模型赋值时allowField不是万能防火墙
很多人以为开了allowField(['name', 'email'])就能挡住JSON注入字段,其实不然。这个配置只过滤字段名,不处理值的内容;如果攻击者传{"name":"x","email":"y","__method":"POST"},__method会被直接丢弃,但若他传{"name":"x","email":"y","status":"1","_method":"PUT"},而你又恰好在模型里写了protected $type = ['_method' => 'string'],那_method就进了类型转换流程,可能干扰路由或中间件判断。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 显式声明可接收字段:用
only(['name', 'email'])替代allowField,它更严格,且不依赖模型层的$type或$validate - 禁止魔术方法名字段:在接收后立即用
array_filter剔除以__开头的键:array_filter($data, function($k) { return strpos($k, '__') !== 0; }, ARRAY_FILTER_USE_KEY) - 警惕
saveAll批量写入:它会循环调用单条save,但若传入的是嵌套JSON数组(如[{"name":"a"},{"name":"b"}]),需先对每个子数组单独做键名校验,不能只校验外层数组
Request::param()和Request::post()行为差异很关键
Request::param()会合并GET、POST、PUT、PATCH等所有参数,并自动尝试JSON解析(取决于json_bind配置);而Request::post()只取原始POST body,不解析、不合并、不触发JSON解码——它是真正“裸”的输入源。很多漏洞就出在误用param()接收JSON请求体,结果框架自动解析后混入了其他来源参数。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 纯API接口统一用
Request::post(false)(false表示不自动解析),再自己调用json_decode控制过程 - 若必须用
param(),务必配合except()剔除敏感键:request()->param('', '', false)->except(['_method', '__construct', '__destruct']),第三个参数false表示不进行变量修饰(即不过滤点号、中划线等) - 注意
Request::post()在application/x-www-form-urlencoded下返回的是已解析的关联数组,在application/json下才返回原始字符串——这点容易混淆,建议统一用input('body')获取原始body更可靠
自定义验证规则比Validate内置规则更可控
ThinkPHP的Validate类支持rule数组,但默认规则(如require、number)只校验值,不校验键名合法性或结构深度。攻击者可以构造{"data":{"a":1,"b":2,"c":{...200层嵌套...}}},验证器照常通过,直到json_decode阶段才崩溃或被绕过。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 写一个
json_structure验证规则,检查数组深度、键名是否含双下划线、总键数是否超限(比如不超过50个) - 在验证前先跑一次轻量级预检:
if (strlen($json) > 1024 * 100) { throw new ValidateException('JSON too large'); },避免大payload拖垮服务 - 不要在验证规则里调用
json_decode——验证器可能被多次执行,重复解析浪费CPU;应在验证前一次性解析并缓存结果
最易被忽略的一点:JSON注入防御不是单点问题,它横跨请求解析、参数过滤、模型赋值、序列化输出四个环节。任何一个环节松动,前面做的都可能白费。尤其是当项目用了第三方SDK或自定义中间件接管了input流程时,得重新确认它们是否重置了json_bind或悄悄做了自动解析。


















