核心是建立输入控制→解析约束→结构校验→使用隔离四层防线:显式限制深度(如32层)、启用json_throw_on_error、预检大小与合法性、清洗bom及控制符、校验键名与嵌套结构、禁用框架自动解析。

修复 PHP JSON 解析相关安全漏洞,核心不是“修一个函数”,而是建立输入控制→解析约束→结构校验→使用隔离的四层防线。重点不在让 json_decode() 更强大,而在于不让它面对不可信、未清洗、无边界的原始数据。
严格限制解析深度与启用异常模式
深层嵌套是拒绝服务(DoS)和栈溢出的直接入口。PHP 默认允许 512 层嵌套,但绝大多数业务逻辑根本不需要超过 32 层。
- 始终显式传入
$depth参数:用json_decode($raw, true, 32, JSON_THROW_ON_ERROR)替代裸调用 - 禁用
ini_set('max_nesting_level', ...):它影响全局且不精准,json_decode()的第三个参数才是请求级精准控制点 - PHP JSON_THROW_ON_ERROR?必须手动检查:
if (json_last_error() !== JSON_ERROR_NONE) { throw new InvalidArgumentException(json_last_error_msg()); }
预检原始字符串:大小、格式、编码、非法字符
在调用 json_decode() 前,先对原始字符串做轻量但关键的过滤和判断。
- 大小限制:用
strlen($raw) > 2 * 1024 * 1024拒绝超过 2MB 的输入,防内存耗尽 - 快速合法性验证:PHP 7.3+ 推荐用
json_validate($raw),比json_decode()更快更轻,不产生中间变量 - 清理 BOM 头:
$raw = ltrim($raw, "\xEF\xBB\xBF"); - 剔除 ASCII 控制符:
$raw = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $raw);(这些字符在 JSON 字符串中非法,却常被恶意注入) - 强制转为 UTF-8:
$raw = mb_convert_encoding($raw, 'UTF-8', 'UTF-8');,避免因编码检测失败导致静默解析失败
解析后结构语义校验与键名清洗
JSON 解析成功 ≠ 数据安全。攻击者可构造合法 JSON,但含危险键名、恶意嵌套或绕过字段白名单的结构。
- 立即清除魔术键名:如
array_filter($data, fn($k) => strpos($k, '__') !== 0, ARRAY_FILTER_USE_KEY)剔除所有以__开头的键(防框架魔术方法/属性利用) - 逐层校验嵌套结构:若接口约定接收
[{"id":1},{"id":2}],不能只校验外层数组,还要对每个子项执行isset($item['id']) && is_int($item['id']) - 禁止直接用于模型赋值或
extract():即使框架有allowField,也无法防御已声明在验证规则或类型定义中的危险字段
规避框架自动解析陷阱
ThinkPHP、Laravel 等框架常默认开启 JSON 自动绑定,但行为不可控、时机不可见,极易引入隐性风险。
- ThinkPHP 中关闭自动 JSON 绑定:
'json_bind' => false写入app.php配置 - 优先用
Request::post(false)或file_get_contents('php://input')获取原始 body,自己掌控解析时机与参数 - 避免使用带类型后缀的便捷方法(如
input('param/json'))处理不可信 JSON 输入——它不做结构校验,也不过滤键名,纯属误导性便利
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











