直接使用unserialize()解析用户输入会导致反序列化漏洞,可触发任意类的__wakeup()或__destruct()执行命令、读文件等;白名单过滤易因第三方库危险类而失效;推荐改用json_decode()或php 7.4+的unserialize($data, ['allowed_classes' => false])。

PHP接口里用 unserialize() 会出什么问题
直接用 unserialize() 解析用户传来的数据,等于把反序列化入口完全暴露给调用方——只要构造恶意 payload,就能触发任意类的 __wakeup() 或 __destruct(),进而执行命令、读文件、写 shell。线上接口一旦开启这个函数且未校验输入,基本等同于开门揖盗。
为什么不能只靠白名单类名过滤
白名单看似保险,但实际容易漏:比如你只允许 User 和 Post 类,但攻击者可能利用已加载的第三方库中带危险魔术方法的类(如某些旧版 Monolog、SwiftMailer 组件),这些类不在你的白名单里,却已被自动加载。更麻烦的是,PHP 7.4+ 的 unserialize() 默认禁用动态类名解析,但若配置了 unserialize_callback_func 或用了 __autoload,风险依然存在。
推荐做法是:彻底不用 unserialize(),改用安全替代方案:
- 前端传 JSON,后端用
json_decode()—— 它只解析基础类型,不触发任何对象逻辑 - 必须兼容老协议时,用
unserialize($data, ['allowed_classes' => false])(PHP 7.4+),强制禁止所有类实例化,只返回数组/字符串/数字等原生结构 - 若真要保留对象语义,改用
igbinary_unserialize()(需扩展)或自定义解析器,但前提是数据来源绝对可信
JSON 替代方案下怎么处理“类结构”语义
很多场景所谓“反序列化”,其实只是想把 JSON 字符串转成带方法的对象,比如 {"name": "Alice", "role": "admin"} → User 实例。这时别硬套 unserialize(),而是手动映射:
$data = json_decode($rawInput, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new InvalidArgumentException('Invalid JSON');
}
$user = new User();
$user->name = $data['name'] ?? '';
$user->role = $data['role'] ?? 'user';
// 或用构造函数 / fromArray() 静态方法封装
这样控制权在你手里:字段可校验、类型可转换、缺失键可设默认值,也不会意外触发未知逻辑。
如果 legacy 系统真绕不开 unserialize()
极少数情况(如对接十几年前的 Java/PHP 混合系统),对方坚持发序列化字符串。此时必须做到三点:
- 输入先过正则:
/^[aO]:\d+:/初筛(排除明显非 PHP 序列化格式) - 用
unserialize($data, ['allowed_classes' => ['WhitelistClass1', 'WhitelistClass2']])显式限定(注意:空数组[]表示允许全部类,不是禁止) - 反序列化后立刻检查对象属性是否符合预期(如
$obj instanceof WhitelistClass1 && !property_exists($obj, "\0*\0dangerous"))
真正难防的不是技术多复杂,而是有人把 allowed_classes 写成 ['*'] 或干脆留空,还觉得“我加了白名单”。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











