序列化 :将对象转换为字符串以便存储传输的一种方式。
反序列化:将字符串转换为对象供程序使用。
在PHP中序列化和反序列化对应的函数分别为serialize()和unserialize()。
class Stu{ public $name; public $age; public $addr; public function demo(){ echo "你好啊!\n"; } } $stu=new Stu(); $stu->name="aaa"; $stu->age=34; $stu->addr="上海"; echo "对象初始值:\n"; print_r($stu); $stus=serialize($stu); //序列化 echo "\n对象序列化后的值:\n"; print_r($stus); $stb=unserialize($stus); //反序列化 echo "\n\n对象反序列化后的值:\n"; print_r($stb); ?>运行结果
魔术方法是PHP面向对象中特有的特性。它们在特定的情况下被触发,都是以双下划线开头,利用魔术方法可以轻松实现PHP面向对象中重载(Overloading即动态创建类属性和方法)。 问题就出现在重载过程中,执行了相关代码。
__construct() :构造函数,当创建对象时自动调用。
__destruct():析构函数,在对象的所有引用都被删除时或者对象被显式销毁时调用,当对象被销毁时自动调用。
__wakeup():进行unserialize时会查看是否有该函数,有的话有限调用,初始化对象。
__ toString():当一个类被当成字符串时会被调用。
__sleep():当一个对象被序列化时调用,可与设定序列化时保存的属性。
- class Stu{
- public $name;
- public $age;
- public $add;
-
- function __construct(){
- print "构造函数".PHP_EOL; //换行符
- }
-
- function __destruct(){
- print "析构函数\n";
- }
-
- function __wakeup(){
- print "\n返序列化函数调用初始化函数wakeup()";
- }
-
- /*让应用程序能够将类作为一个字符串输出(echo $obj),而且
- 其他类也可能定义了一个类允许 __toString读取某个文件。*/
- function __toString(){
- return $this->name."toString字符串";
- }
-
- /*function __sleep(){
- print "序列化时调用该函数";
- }*/
- }
-
- //创建几次对象,执行几次构造函数
- $stu=new Stu();
-
- $stu->name="fpp";
- $stu->age=34;
- $stu->add="dfdgg";
-
- $stus=serialize($stu); //序列化
- echo "\n对象序列化后的值:\n";
- print_r($stus);
-
- echo "\n\n执行反序列化";
- $stb=unserialize($stus); //反序列化
- echo "\n\n对象反序列化后的值:\n";
- print_r($stb);
-
- print "\n输出对象值:\n$stu";
-
- //销毁几个对象,执行几次析构函数
- ?>
输出结果
当程序在进行反序列化时,会自动调用一些函数,例如__wakeup(),__destruct()等函数,但是如果传入函数的参数可以被用户控制的话,用户可以输入一些恶意代码到函数中,从而导致反序列化漏洞。
反序列化时unserialize()函数会自动调用wakeup(),destruct(),函数,当有一些漏洞或者恶意代码在这些函数中,当我们控制序列化的字符串时会去触发他们,从而达到攻击的目的。
-
- class LogFile
- {
- //日志文件名
- public $filename = 'error.log';
- //存储日志文件
- function LogData($text)
- {
- //输出需要存储的内容
- echo 'log some data:'.$text.'
'; - file_put_contents($this->filename, $text,FILE_APPEND);
- }
- //删除日志文件
- function __destruct()
- {
- //输出删除的文件
- echo '析构函数__destruct 删除新建文件'.$this->filename;
- //绝对路径删除文件
- unlink(dirname(__FILE__).'/'.$this->filename);
- }
- }
-
-
- $logfile = unserialize($_GET['logfile']);
- //O:7:"LogFile":1:{s:8:"filename";s:9:"error.log";}
- echo "
"
; - var_dump($logfile);
- ?>
执行结果
发现正常删除,但如果我们修改参数,让其删除其他的文件呢?
- class chybeta
- {
- public $test = '123';
- function __wakeup()
- {
- $fp = fopen("shell.php","w") ;
- fwrite($fp,$this->test);
- fclose($fp);
- }
- }
- $class = @$_GET['test'];
- print_r($class);
- echo "";
- $class_unser = unserialize($class);
-
- // 为显示效果,把这个shell.php包含进来
- require "shell.php";
- ?>
- 不要把用户的输入或者是用户可控的参数直接放进反序列化的操作中去。
- 在进入反序列化函数之前,对参数进行限制过滤。