PHP提供serialize和unserialize函数将任意类型的数据转换成string类型或者从string类型还原成任意类型。当unserialize函数的参数被用户控制的时候,就会形成反序列化漏洞。如著名的CVE-2016-7124漏洞,执行unserialize函数时,当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行。
序列化
在PHP中,序列化用于存储或传递PHP的值的过程,同时不丢失其类型和结构。序列化函数serialize原型如下
string serialize ( mixed $value )
序列化不同类型得到的字符串格式如下:
- String: s:size:value:
- Integer: i:value:
- Boolean: b:value; (value的值只能为0或1)
- Null: N;
- Array: a:size:{key-difinition;value-difinition…repeated per element}
- Object: O:object-name-length:object-name:object-size:{s:property-name-length:property-name:property-definition;…repeated per property}
如果要序列化的类型是integer或者float类型,若value为正数或0,则在序列化后的字符串的value前添加+不会对其反序列化造成影响。
示例代码:
class CC {
public $data;
private $pass;
public function __construct($data, $pass)
{
$this->data = $data;
$this->pass = $pass;
}
}
$number = 34;
$str = 'uusama';
$bool = true;
$null = NULL;
$arr = array('a' => 1, 'b' => 2);
$cc = new CC('uu', true);
var_dump(serialize($number));
var_dump(serialize($str));
var_dump(serialize($bool));
var_dump(serialize($null));
var_dump(serialize($arr));
var_dump(serialize($cc));
输出结果:
string(5) "i:34;"
string(13) "s:6:"uusama";"
string(4) "b:1;"
string(2) "N;"
string(30) "a:2:{s:1:"a";i:1;s:1:"b";i:2;}"
string(52) "O:2:"CC":2:{s:4:"data";s:2:"uu";s:8:" CC pass";b:1;}"
注意: 序列化对象时,不会保存常量的值。对于父类的变量,则会保留。
反序列化
反序列化unserialize函数原型:
mixed unserialize(string $str)
反序列化函数unserialize()用于将单一的已经序列化的变量转换回PHP的值。
- 如果传递的字符串不可解序列化,则返回FALSE。
- 返回的是转换后的值,可以为integer、float、string、array、object
- 若被反序列化的变量是一个对象,在成功构造对象之后,如果我们编写的有__wakeup()魔术方法的话,PHP会自动地试图去调用。