php反序列化漏洞本质是反序列化过程中自动触发魔术方法(如__wakeup、__destruct),攻击者通过控制输入字符串重建恶意对象,结合pop链将可控属性传递至危险操作;防御需禁用用户输入反序列化、用json替代、白名单限制类、审计魔术方法逻辑。

反序列化漏洞的底层原理:不是解析出问题,是“重建时自动执行”
PHP反序列化漏洞不源于unserialize()函数本身会“错解”数据,而在于它在内存中重建对象的过程中,会**自动触发特定魔术方法**。这些方法由PHP内核硬编码调用,开发者无法绕过,也不需要显式调用。
关键钩子有两个:
- __wakeup():对象完成反序列化后立即触发(PHP 7.4及以前)
- __destruct():对象生命周期结束、被销毁时触发(脚本退出或变量被unset时)
只要攻击者能控制传入unserialize()的字符串,并让其还原成一个定义了上述方法的类实例,就可能借这些方法中的业务逻辑(如文件写入、命令拼接、动态调用)达成恶意效果。注意:__construct()在反序列化时不会被调用,这点常被误判。
POP链构造:把散落的“危险逻辑”串成一条执行路径
单个魔术方法往往不够直接执行命令,比如__destruct()里只有一句file_put_contents($this->filename, $this->content)。这时攻击者需控制$this->filename和$this->content——但这两个属性从哪来?答案是:通过其他类的其他方法间接赋值或触发。
POP链(Property Oriented Programming)就是利用多个类之间已有的方法调用关系,把可控属性“传递”过去,最终导向危险操作。典型构造思路:
- 找一个入口点:含
__wakeup或__destruct且逻辑可被影响的类 - 找中间跳板:如
__toString()(当对象被当作字符串使用时触发)、__get()(访问未定义属性时触发)、__call()(调用不存在方法时触发) - 找终点:能执行系统命令、写文件、发起HTTP请求等敏感操作的方法,例如
system()、file_put_contents()、call_user_func()
链的形成依赖真实代码中类的定义。没有现成可用的链,再完美的payload也无效。这也是为什么审计时必须结合项目源码,而非仅看是否存在unserialize()调用。
防御方案:堵入口 + 断链条 + 换方案
防御不能只靠“别用unserialize()”,而要分层落地:
-
禁用用户可控输入的反序列化:所有
unserialize($_GET[…])、unserialize($_COOKIE[…])、unserialize($_POST[…])必须删除或替换。这是最高优先级 -
用json替代序列化传输:JSON格式无类型信息,不触发任何魔术方法,
json_encode()/json_decode()是更安全的通用方案 -
白名单反序列化(PHP 7.4+):启用
unserialize($data, ['allowed_classes' => ['WhitelistClass']]),严格限制可重建的类名 -
审计魔术方法逻辑:检查所有
__wakeup、__destruct、__toString等方法,避免在其中使用eval、system、include、file_put_contents等高危操作;若必须用,确保参数完全不可控或经过强校验 -
禁用危险原生类(可选加固):通过
disable_classes配置项禁用SoapClient、Phar等曾被用于POP链的类(注意兼容性)
真正可靠的防护,是让反序列化行为彻底脱离用户输入边界。哪怕保留unserialize(),也只用于可信内部缓存或配置加载,且数据来源与结构完全受控。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











