一文了解反序列化漏洞
字数 1195 2025-08-12 11:33:45
PHP反序列化漏洞详解
一、序列化与反序列化基础
1. 序列化概念
序列化是将对象或数组转换为字节序列的过程,类似于游戏中的存档操作。在PHP中,使用serialize()函数实现序列化。
序列化函数语法:
string serialize ( mixed $value )
示例:
$sites = array('I', 'Like', 'PHP');
echo serialize($sites);
// 输出: a:3:{i:0;s:1:"I";i:1;s:4:"Like";i:2;s:3:"PHP";}
class man {
public $name = "xiaocui";
public $sex = "man";
private $age = 26;
}
$M = new man();
echo serialize($M);
// 输出: O:3:"man":3:{s:4:"name";s:7:"xiaocui";s:3:"sex";s:3:"man";s:8:"man age";i:26;}
2. 反序列化概念
反序列化是将字节序列恢复为对象的过程,类似于游戏中的读档操作。在PHP中,使用unserialize()函数实现反序列化。
反序列化函数语法:
mixed unserialize ( string $str )
示例:
$ser = 'a:3:{i:0;s:1:"I";i:1;s:4:"Like";i:2;s:3:"PHP";}';
var_dump(unserialize($ser));
// 输出原始数组
3. 序列化与反序列化的作用
- 持久化存储:将对象字节序列永久保存在磁盘中,节省内存空间
- 提高传输效率:在网络传输中直接传输字节序列而非对象
二、PHP魔术方法
PHP中以双下划线__开头的方法为魔术方法,在特定情况下自动调用。
1. 常见魔术方法
__construct()
- 在创建新对象时调用,用于初始化
public function __construct() {
echo "类被实例化时调用我!";
}
__destruct()
- 当对象的所有引用被删除或对象被销毁时调用
public function __destruct() {
echo "对象被销毁时调用我!";
}
__wakeup()
- 在使用
unserialize()时首先调用
public function __wakeup() {
echo "反序列化时首先调用我!";
}
__toString()
- 当对象被当作字符串处理时调用
public function __toString() {
return "对象被当作字符串处理";
}
__sleep()
- 在使用
serialize()时首先调用
public function __sleep() {
return array("name", "age"); // 必须返回属性数组
}
__invoke()
- 当尝试以函数方式调用对象时调用
public function __invoke() {
echo "对象被当作函数调用";
}
__call()
- 当调用不存在或不可访问的方法时调用
public function __call($method, $args) {
echo "调用了不存在的方法: $method";
}
__set() / __get()
__set(): 给不可访问属性赋值时调用__get(): 读取不可访问属性时调用
public function __set($name, $value) {
echo "设置不可访问属性: $name";
}
public function __get($name) {
echo "读取不可访问属性: $name";
}
__isset() / __unset()
__isset(): 对不可访问属性调用isset()或empty()时调用__unset(): 对不可访问属性调用unset()时调用
三、反序列化漏洞
1. 漏洞利用条件
unserialize()函数的参数可控- 存在可利用的类,且类中有魔术方法
2. 漏洞示例
示例1: XSS漏洞
class demo {
public $arg1 = "0";
public function __destruct() {
echo $this->arg1; // 输出用户可控内容
}
}
$a = $_GET['arg']; // 用户可控输入
$unser = unserialize($a);
攻击者可构造恶意序列化字符串实现XSS攻击。
示例2: RCE漏洞
class demo {
public $arg1 = "0";
public function __destruct() {
eval($this->arg1); // 执行用户可控代码
}
}
$a = $_GET['arg'];
$unser = unserialize($a);
攻击者可构造恶意序列化字符串实现远程代码执行。
3. __wakeup()绕过
在PHP版本<=5.6.25或<=7.0.11时,当序列化字符串中对象属性数量大于实际属性数量时,可绕过__wakeup()方法。
示例:
class demo {
public $arg1 = "0";
public function __wakeup() {
$this->arg1 = ''; // 正常情况下会清空arg1
}
public function __destruct() {
eval($this->arg1); // 危险操作
}
}
// 正常序列化
$normal = 'O:4:"demo":1:{s:4:"arg1";s:10:"phpinfo();";}';
// 绕过序列化
$bypass = 'O:4:"demo":2:{s:4:"arg1";s:10:"phpinfo();";s:4:"arg2";s:1:"1";}';
四、防御措施
- 不要反序列化不可信数据
- 使用
json_encode()/json_decode()替代序列化 - 对反序列化操作进行严格的白名单控制
- 及时升级PHP版本,修复已知漏洞
- 在魔术方法中避免危险操作
- 对用户输入进行严格过滤和验证
五、总结
PHP反序列化漏洞是一种严重的安全威胁,攻击者可以利用它实现XSS、RCE等多种攻击。理解序列化/反序列化机制、魔术方法的调用时机以及漏洞利用条件,对于防范此类攻击至关重要。开发者应当遵循安全编码规范,避免直接反序列化用户可控输入,并及时更新PHP版本以修复已知漏洞。