深入浅出PHP序列化与反序列化:原理、漏洞与实战防御
目录导读
- 什么是PHP序列化与反序列化?
- 序列化与反序列化的核心函数与格式解析
- 魔术方法:反序列化的隐形开关
- 反序列化漏洞(POP链)的成因与攻击场景
- 真实案例:从反序列化到远程代码执行
- 如何安全地使用序列化:防御策略清单
- 常见问题答疑(FAQ)
什么是PHP序列化与反序列化?
在Web开发中,我们经常需要将对象或数组转换为可存储、可传输的字符串形式,这个过程叫序列化(Serialization);而将字符串还原为原始对象或数组的过程则称为反序列化(Unserialization),PHP通过内置函数serialize()和unserialize()完成这两个操作。

序列化让复杂的数据结构变得“扁平化”,便于存入Session、缓存、数据库或通过API传输,但这也是一把双刃剑——如果反序列化操作了不可信的用户输入,就会引发严重的安全问题。
序列化与反序列化的核心函数与格式解析
PHP语言中,serialize()会输出一种特定格式的字符串,一个对象User{ name:"admin"; isAdmin:false }会被序列化为:
O:4:"User":2:{s:4:"name";s:5:"admin";s:7:"isAdmin";b:0;}
解析这个格式:
O代表对象,4为类名长度,User为类名。2表示有2个属性。s:4:"name"表示字符串属性名,长度为4。b:0表示布尔值false。
还有i(整数)、a(数组)、N(NULL)等类型标识,理解此格式,是手动构造攻击载荷(Payload)的基础。
魔术方法:反序列化的隐形开关
PHP中的魔术方法(如__construct、__destruct、__wakeup、__sleep等)在特定操作时自动执行,其中最关键的两个:
__sleep():在serialize()执行前调用,允许清理对象并返回要序列化的属性数组。__wakeup():在unserialize()执行后立即调用,常用于重新建立数据库连接等恢复操作。
攻击视角:如果攻击者能控制传入unserialize()的字符串,他们就可以控制对象属性值,进而触发__wakeup()或__destruct()中的危险方法(如文件删除、命令执行)。
反序列化漏洞(POP链)的成因与攻击场景
成因:当开发者将用户可控的输入(如Cookie、请求参数、签名数据)直接传递给unserialize()时,攻击者可以精心构造一个恶意对象链(Property-Oriented Programming,即POP链),通过一连串已存在的类方法逻辑,达到命令执行、文件读取、SSRF等目的。
典型攻击场景:
- 将序列化数据存入Cookie,读取时未验证来源。
- 多语言交互中,从外部API获取数据后直接反序列化。
- 使用
unserialize()处理数据库中的持久化对象,但数据库被SQL注入篡改。
真实案例:从反序列化到远程代码执行
以经典的ThinkPHP 5.0.x反序列化RCE漏洞为例:
- 攻击者找到一个接受
data参数的入口,后端代码执行unserialize(base64_decode($data))。 - 攻击者借助框架中的
Output类和Process类,构造一条以__destruct为起点的POP链。 - 最终链中调用
proc_open()执行系统命令,实现远程代码执行。
该案例说明,即使没有明显危险函数,攻击者也能利用框架内已有类的方法拼凑出攻击链。
如何安全地使用序列化:防御策略清单
- 永远不要反序列化不可信数据:这是最核心原则,如果可能,优先使用
json_encode/json_decode替代。 - 使用
allowed_classes参数:unserialize($data, ['allowed_classes' => false])可禁止对象创建,或限制为白名单类。 - 进行HMAC签名校验:对序列化字符串附加签名,确保数据未被篡改。
- 升级框架与依赖库:及时修补已知POP链漏洞。
- 配置
open_basedir与禁用危险函数:降低漏洞利用后的损害。
常见问题答疑(FAQ)
Q1:serialize()和json_encode()有什么区别?
A:serialize()能保留对象的类信息,但存在安全隐患;json_encode()仅保留公开属性且不触发魔术方法,更安全、跨语言兼容性更好。
Q2:为什么__wakeup()会绕过某些检查?
A:因为__wakeup()在属性完全恢复后才被调用,攻击者可以利用此顺序,在检查逻辑执行前把恶意属性值写进对象。
Q3:如何快速检测一个反序列化漏洞?
A:尝试将O:1:"A":0:{}这类测试对象传入入口,观察响应时间、错误信息或文件系统变化,配合工具(如PHPGGC)生成Payload进行验证。
PHP序列化与反序列化是强大的功能,但必须建立在“信任边界”清晰的架构上,开发者应时刻保持警惕:边界上的每一个输入都不可信,任何将字符串还原为对象的动作,都等同于执行了一段隐藏的代码逻辑。 通过本文的梳理,希望你能从原理到实战,全面加固自己的应用安全防线。