发布于2022年10月15日2年前 什么是序列化 序列化可以实现将对象压缩并格式化,方便数据的传输和存储。 为什么要序列化? PHP 文件在执行结束时会把对象销毁,如果下次要引用这个对象的话就很麻烦,所以就有了对象序列化,实现对象的长久存储,对象序列化之后存储起来,下次调用时直接调出来反序列化之后就可以使用了。 学习序列化要了解的基本内容。 类(Class): 类的定义包含了数据的形式以及对数据的操作。 对象:对象是类的实例。 方法:类中定义的函数。 <?php //创建一个类 Test class Test { //定义 3 个属性,最后序列化后看一下这 3 个属性序列化后的结果。 private $a = "private"; public $b = "public"; protected $c = "protected"; } //创建一个对象,对象是类的实例。 $test = new Test(); //序列化 test 这个对象 $data = serialize($test); //打印序列化后的对象 echo $data; ?> 展示结果(前提条件,php只有再apache下可以运行,之前我创建了txt,发现无法运行,避免踩坑) 序列化之后的结果,每个字符都具有具体含义O:4:"Test":3: {s:7:"Testa";s:7:"private"; s:1:"b";s:6:"public"; s:4:"*c";s:9:"protected";} O Object 对象 4 名字长度为 4 Test(对象名称) 3 表示对象中存在3个属性 s:7:"Testa"; 变量名字 s:7:"private"; 值 s string 字符串 7 变量名长度为7 private私有的,会在Testa前后添加\00 ,即\X00Test\X00a (因此长度为7) s string 字符串 7 变量的值长度为7 private 值 s:1:"b"; s:6:"public" s string 1 名字长度 1 b 变量名字 s:4:"*c"; s:9:"protected"; s string 4 名字长度 4 *c 变量名字 \x00*\x00c 序列化的过程: 通过classTest创建一个对象,然后把这个对象进行序列化,打印出来 反序列化: PHP 反序列化漏洞又叫做 PHP 对象注入漏洞,成因在于代码中的 unserialize() 接收的参数可控,从上面的例子看,这个函数的参数是一个序列化的对象,而序列化的对象只含有对象的属性,那我们就要利用对对象属性的篡改实现最终的攻击 代码如下: (注意:Testa和*c的部分要留出空格来,与前面的的长度相符合) 输出要用var_dump而不是echo <?php $data = 'O:4:"test":3:{s:7:" Test a";s:7:"private";s:1:"b";s:6:"public";s:4:" * c";s:9:"protected";}'; $free = unserialize($data); var_dump($free); ?> 结果如下: object(__PHP_Incomplete_Class)#1 (4) { ["__PHP_Incomplete_Class_Name"]=> string(4) "test" [" Test a"]=> //名字 string(7) "private" //值 ["b"]=> //名字 string(6) "public" //值 [" * c"]=> //名字 string(9) "protected" //值 } 反序列化造成漏洞的原因: 反序列化一般都是用户传递过来恶意代码,通过反序列化执行 反序列化的字符串用户可以控制 内容长度改变以后,前面的长度也需要跟着改变 序列化-魔术方法 魔术方法介绍: 方法:类中定义的函数。 上面我们演示了如何进行序列化和反序列化,但是我们仅使用了属性,也就是变量,变量我们可以当 做数据来看待,而编程就是对数据进行一些列的操作和处理,操作和处理数据的过程我们一般通过函数来 定义,函数在面向对象编程中我们一般称之为方法,所以后续如果老师说到方法就等于说的是函数功能。 特殊的方法-魔术方法。 PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法,这些都是 PHP 内置的方法。 __construct 当一个对象创建时被调用, __destruct 当一个对象销毁时被调用, __wakeup() 使用 unserialize 时触发 __sleep() 使用 serialize 时触发 __call() 在对象上下文中调用不可访问的方法时触发 __callStatic() 在静态上下文中调用不可访问的方法时触发 __get() 用于从不可访问的属性读取数据 __set() 用于将数据写入不可访问的属性 __isset() 在不可访问的属性上调用 isset()或 empty()触发 __unset() 在不可访问的属性上使用 unset()时触发 __toString() 把类当作字符串使用时触发,返回值需要为字符串 __invoke() 当脚本尝试将对象作为函数调用时触发
创建帐户或登录后发表意见