PHP反序列化漏洞需确认两点:是否调用unserialize()处理用户可控输入,是否存在可利用的魔术方法链;须人工核验调用路径、类中危险魔术方法及allowed_classes配置,PHP 8.1下还需注意类型校验与新风险点。

PHP 8.1 站点扫描出反序列化漏洞,不能只看工具报错就下结论。关键要确认两点:是否真在运行时调用了 unserialize() 处理用户可控输入;是否存在可利用的魔术方法链(POP chain)。核验必须结合代码上下文、调用路径和数据流向。
定位真实 unserialize() 调用点
很多扫描结果是“疑似”——比如只是存在类定义或函数声明,但未被实际调用。需人工确认:
- 搜索项目中所有
unserialize(的调用,重点检查参数是否来自$_GET、$_POST、$_COOKIE、$_SESSION或文件读取(如file_get_contents()) - 排除日志、缓存、测试代码等非生产路径;若调用包裹在
if (false)或注释中,不构成风险 - 注意间接调用:某些框架方法(如 Laravel 的
SessionHandler::read()、ThinkPHP 的input()配合特定解析器)底层可能触发反序列化
检查类与魔术方法是否可被构造利用
有 unserialize() 不等于有漏洞,必须存在可被攻击者控制属性并触发危险行为的类:
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
- 查看报告指向的类,确认是否含
__wakeup、__destruct、__toString等魔术方法,且方法体内执行了危险操作(如system()、file_put_contents()、call_user_func()) - 检查属性是否为
public,或是否可通过反序列化控制其值(protected/private属性需按正确格式构造,如s:7:"\0*\0prop") - 验证是否启用
allowed_classes参数:若调用写成unserialize($data, ['allowed_classes' => false])或限定白名单(如['SafeClass']),则大部分 POP 链失效
复现验证:构造最小 PoC 数据
静态扫描无法替代动态验证。建议在开发环境尝试构造并触发一次:
立即学习“PHP免费学习笔记(深入)”;
- 从报告类中提取关键属性名和魔术方法逻辑,手工拼一个最简序列化字符串(例如
O:12:"VulnLogger":1:{s:8:"logFile";s:9:"/tmp/test";}) - 通过 GET/POST 提交该字符串到疑似接口,观察是否写入文件、执行命令或报错回显(注意关闭
display_errors,改查日志) - 若使用 Phar 伪协议(如
phar://xxx.jpg/exploit.ser),需确认目标开启了phar.readonly=Off且扩展可用
区分 PHP 8.1 特有干扰项
PHP 8.1 对反序列化做了更严格的类型校验,部分旧版漏洞在 8.1 下已不可用,但会引入新风险点:
- 检查是否误报:如
mysql_connect()类废弃函数调用,PHP 8.1 已移除,直接报致命错误,无需反序列化即可阻断,不属于此漏洞范畴 - 关注 8.1 新增风险:未加
#[\ReturnTypeWillChange]的__wakeup若返回类型与父类不一致,可能绕过异常处理,导致后续逻辑异常执行 - 确认 session 反序列化是否启用:PHP 8.1 默认
session.serialize_handler=php_serialize,比旧版php更安全;若项目强制切回phphandler,则风险回升


















