PHP解析不受信任JSON必须多层设防:显式限制深度(如32层)、启用JSON_THROW_ON_ERROR、预检大小与合法性、清洗BOM及控制符、校验键名与嵌套结构、避免框架自动解析。

解析不受信任的JSON数据,不能只靠 json_decode() 一招走天下。关键在于控制输入边界、限制解析行为、校验结构语义,并在多层设防。
严格限制嵌套深度与解析选项
深层嵌套是DoS攻击的常见入口。PHP默认允许512层嵌套(7.3+),但多数业务根本用不到20层。应显式传入合理深度,并启用异常模式:
- 使用
json_decode($raw, true, 32, JSON_THROW_ON_ERROR)—— 第三个参数设为32或更低,第四个参数确保失败立刻抛出JsonException - 避免依赖
ini_set('max_nesting_level', ...),它影响全局且不精确;json_decode的$depth参数才是精准控制点 - 若需兼容旧版本(PHP < 7.3),则必须手动检查:
if (json_last_error() !== JSON_ERROR_NONE) { throw new InvalidArgumentException(json_last_error_msg()); }
预检原始字符串合法性与大小
解析前就该拦截明显危险的数据:
- 用
strlen($raw) > 2 * 1024 * 1024拒绝超过2MB的请求体,防止内存耗尽 - 调用
json_validate($raw)(PHP 7.3+)快速判断格式是否合法,比直接json_decode更轻量 - 清洗非法字符:去除BOM头(
ltrim($raw, "\xEF\xBB\xBF"))、过滤控制符(preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $raw))
过滤危险键名与结构语义校验
JSON本身无“字段白名单”概念,解析后必须主动清理:
立即学习“PHP免费学习笔记(深入)”;
- 立即剔除以
__开头的键名:array_filter($data, fn($k) => strpos($k, '__') !== 0, ARRAY_FILTER_USE_KEY) - 对嵌套数组逐层校验:若接收的是列表(如
[{"name":"a"},{"name":"b"}]),不能只校验外层数组,而要对每个子项单独执行键名校验和类型检查 - 避免直接用于模型赋值或
extract();即使开了allowField,也挡不住已声明在$type或$validate中的魔术字段
区分数据来源,慎用自动解析机制
框架常自带JSON解析逻辑,但未必按你预期工作:
- ThinkPHP中
input()默认开启json_bind,会自动将application/json请求体转成数组——若不需要,应在app.php中设'json_bind' => false - 优先使用
Request::post(false)获取原始body,自己控制解析时机与方式,避开框架自动合并、自动解码带来的不确定性 - 不用
input('param/a')这类带类型后缀的写法处理JSON输入,它不校验结构,也不过滤键名,纯属误导性便利



















