php反序列化入门

一、什么是序列化和反序列化

有时需要把一个对象在网络上传输,为了方便传输,可以把整个对象转化为二进制串,等到达另一端时,再还原为原来的对象,这个过程称之为串行化(也叫序列化)。

类型 过程
序列化 对象—> 字符串
反序列化 字符串—>对象

对象的序列化利于对象的保存和传输 ,也可以让多个文件共享对象。

image-20221025205724327

序列化

序列化函数serialize()

首先我创一个Ctf类 里面写了三个属性 后创建了一个ctfer对象 将Ctf类里的信息进行了改变。如果后面还要用到这个对象,就可以先将这个对象进行实例化。用的时候在反序列化出来就ok了。

1
2
3
4
5
6
7
8
9
10
11
12
<?php
class Ctf{
public $flag='flag{****}';
public $name='cxk';
public $age='10';
}
$ctfer=new Ctf(); //实例化一个对象
$ctfer->flag='flag{adedyui}';
$ctfer->name='Sch0lar';
$ctfer->age='18'
echo serialize($ctfer);
?>
1
2
3
4
5
6
7
8
9
10
//输出结果
O:3:"Ctf":3{s:4:"flag";s:13:"flag{abedyui}";s:4:"name";s:7:"Sch0lar";s:3:"age";s:2:"18";}
O代表对象,因为我们序列化的是一个对象;序列化数组的话则用A来表示
3代表类的名字长三个字符
Ctf 是类名
3代表这个类里有三个属性(三个变量)
s代表字符串
4代表属性名的长度
flag是属性名
s:13:"flag{adedyui}" 字符串,属性长度,属性值

![image (2)](C:\Users\14273\Downloads\image (2).png)

PHP 对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。

public(公有):公有的类成员可以在任何地方被访问。
protected(受保护):受保护的类成员则可以被其自身以及其子类和父类访问。
private(私有):私有的类成员则只能被其定义所在的类访问。

注意:访问控制修饰符不同,序列化后属性的长度和属性值会有所不同,如下所示:

public:属性被序列化的时候属性值会变成 属性名
protected:属性被序列化的时候属性值会变成 \x00*\x00属性名
private:属性被序列化的时候属性值会变成 \x00类名\x00属性名

反序列化

反序列化函数unserialize()。反序列化就是将一个序列化了的对象或数组字符串,还原回去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class Ctf{
public $flag='flag{****}';
public $name='cxk';
public $age='10';
}
$ctfer=new Ctf(); //实例化一个对象
$ctfer->flag='flag{adedyui}';
$ctfer->name='Sch0lar';
$ctfer->age='18'
$str=serialize($ctfer);
echo '<pre>';
var_dump(unserialize($str))
?>
1
2
3
4
5
6
7
8
9
//输出结果
object(Ctf)#2 (3) {
["flag"]=>
string(13) "flag{abedyui}"
["name"]=>
string(7) "Sch0lar"
["age"]=>
string(2) "18"
}

二、为什么会出现安全漏洞

未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL注入,目录遍历等不可控后果。

PHP 存在一些魔术方法,在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法。

1
2
3
4
5
6
7
8
9
10
11
12
__construct(): //构造函数,当对象new的时候会自动调用
__destruct()://析构函数当对象被销毁时会被自动调用
__wakeup(): //unserialize()时会被自动调用
__invoke(): //当尝试以调用函数的方法调用一个对象时,会被自动调用
__call(): //在对象上下文中调用不可访问的方法时触发
__callStatci(): //在静态上下文中调用不可访问的方法时触发
__get(): //用于从不可访问的属性读取数据
__set(): //用于将数据写入不可访问的属性
__isset(): //在不可访问的属性上调用isset()或empty()触发
__unset(): //在不可访问的属性上使用unset()时触发
__toString(): //把类当作字符串使用时触发
__sleep(): //serialize()函数会检查类中是否存在一个魔术方法__sleep() 如果存在,该方法会被优先调用

O%3A1%3A%22A%22%3A1%3A%7Bs%3A4%3A%22file%22%3BO%3A1%3A%22B%22%3A2%3A%7Bs%3A3%3A%22str%22%3BO%3A1%3A%22C%22%3A3%3A%7Bs%3A3%3A%22eee%22%3BO%3A1%3A%22D%22%3A2%3A%7Bs%3A6%3A%22%00D%00ddd%22%3BN%3Bs%3A6%3A%22%00D%00ext%22%3BO%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A3%3A%22aaa%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A130%3A%22verf1sh%0D%0AContent-Type%3Aapplication%2Fx-www-form-urlencoded%0D%0AX-Forwarded-For%3A+127.0.0.1%2C127.0.0.1%0D%0AContent-Length%3A+13%0D%0A%0D%0Atoken%3Dverf1sh%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D%7Ds%3A3%3A%22aaa%22%3Bs%3A12%3A%22who+are+you%3F%22%3Bs%3A3%3A%22ccc%22%3BN%3B%7Ds%3A5%3A%22huang%22%3BN%3B%7D%7D


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!