PHP反序列化漏洞源于将不可信输入直接传给unserialize(),当用户可控序列化字符串且目标类含危险魔术方法时,可构造POP链执行任意代码。

PHP反序列化漏洞不是函数缺陷,而是边界设计失误——核心问题在于把不可信输入直接喂给 unserialize()。只要用户能控制传入的序列化字符串,且目标类中存在可被触发的危险魔术方法(如 __destruct、__wakeup),就极可能被构造POP链执行任意代码。
常见错误写法
这些写法在真实项目中高频出现,且极易被利用:
-
直接反序列化 GET/POST 参数:例如
unserialize($_GET['data'])或unserialize(base64_decode($_POST['payload'])),完全未校验来源与内容 -
缓存读取不设防:ThinkPHP 的
think\cache\driver\File->read()等方法内部直接调用unserialize($content),若缓存文件被污染即触发漏洞 -
Session 处理逻辑松散:使用 Memcache/Redis 存储 session 时,若服务端配置不当或 session 数据被劫持篡改,
session_start()后的反序列化解析环节可能被攻击 -
Phar 协议滥用:攻击者上传伪装成图片的
phar://xxx.jpg/exploit.phar,再通过file_exists()、file_get_contents()等函数触发自动反序列化 -
白名单形同虚设:设置
allowed_classes => []或allowed_classes => false混淆使用;或将高危类(如think\Model、Monolog\Handler\StreamHandler)加入白名单
快速排查路径
重点检查代码中所有 unserialize( 调用点及其上下文是否可控:
- 全局搜索
unserialize(、unserialize (、base64_decode.*unserialize等变体写法,特别注意注释掉但未删除的调试代码 - 检查第三方组件和框架版本:
ThinkPHP 5.x全系已停止维护,6.0–6.2仍存在绕过风险;monolog、swiftmailer等依赖库也多次曝出 POP 链 - 审查 session 配置:
session.serialize_handler是否为php_serialize(推荐),session.use_strict_mode是否启用 - 验证 Phar 相关配置:
phar.readonly = On、allow_url_include = Off必须生效;Web 服务器层(如 Nginx)应拦截含phar://的请求
稳妥修复方案
修复优先级应是“砍入口 > 设白名单 > 换方案”,而非修补链路:
立即学习“PHP免费学习笔记(深入)”;
-
立即替换为 JSON:将所有非必须对象反序列化的场景,统一改为
json_decode($input, true)。JSON 不还原类、不触发魔术方法,天然免疫 -
强制启用 allowed_classes:PHP 7.4+ 环境下,
unserialize($data, ['allowed_classes' => ['MySafeClass']]')是最轻量兜底;设为false可禁用所有类(仅允许数组/标量) -
禁用危险协议与驱动:关闭
phar、filter、data等流封装器;ThinkPHP 中禁用File缓存驱动,改用Redis并确保其数据不可控输入不参与反序列化 -
升级并锁定依赖:ThinkPHP 升级至 6.3+,同时审计
composer.lock,移除已知存在反序列化链的旧版组件(如monolog 1.x、guzzlehttp/ringphp)
防御本质提醒
反序列化漏洞的本质,是把“数据”当成了“代码”来执行。只要业务不需要还原对象实例,就绝不该用 unserialize()。JSON、MessagePack、Protocol Buffers 等格式更安全、更通用、更易审计。别让一行图省事的 unserialize() 成为系统里最深的一颗雷。



















