本文为笃行平常学习记录,web安全php漏洞系列。php
对象的序列化和反序列化做用就再也不赘述,php中序列化的结果是一个php自定义的字符串格式,有点相似json. html
咱们在任何语言中设计对象的序列化和反序列化都须要解决几个问题c++
知道类型还不够,固然还须要知道这个类型所对应具体的值).golang
本文仅仅从php代码角度来解释php中序列化和反序列化的过程.,记住一点序列化和反序列化操做的仅仅是对象的数据,这一点有面向对象开发经验的都应该容易理解.web
php原生提供了对象序列化功能,不像c++ ……^_^. 用起来也很是简单,就两个接口.算法
class fobnn { public $hack_id; private $hack_name; public function __construct($name,$id) { $this->hack_name = $name; $this->hack_id = $id; } public function print() { echo $this->hack_name.PHP_EOL; } } $obj = new fobnn('fobnn',1); $obj->print(); $serializedstr = serialize($obj); //经过serialize接口序列化 echo $serializedstr.PHP_EOL;; $toobj = unserialize($serializedstr);//经过unserialize反序列化 $toobj->print();
fobnn O:5:"fobnn":2:{s:7:"hack_id";i:1;s:16:"fobnnhack_name";s:5:"fobnn";} fobnn
看到第二行的输出,这个字符串就是序列化的结果,这个结构其实很容读懂,能够发现是经过对象名称/成员名称来映射的,固然不一样访问权限的成员序列化以后的标签名称略有不一样.shell
根据我上面讲到的3个问题,那么咱们能够来看看数据库
1.自描述功能json
O:5:"fobnn":2
其中o就表示了object
类型,且类型名称为fobnn, 采用这种格式,后面的2表示了有2个成员对象.数组
关于成员对象,其实也是同一套子描述,这是一个递归的定义.
自描述的功能主要是经过字符串记录对象和成员的名称来实现.
2.性能问题
php序列化的时间性能本文就不分析了,详见后面,但序列化结果其实相似json/bson定义的协议,有协议头,协议头说明了类型,协议体则说明了类型所对应的值,并不会对序列化结果进行压缩.
对应上述说的第二个问题,其实php中也有解决方法,一种是经过魔术方法,第二种则是自定义序列化函数.先来介绍下魔术方法 __sleep
和__wakeup
http://php.net/manual/en/lang...
http://php.net/manual/en/lang...
class fobnn { public $hack_id; private $hack_name; public function __construct($name,$id) { $this->hack_name = $name; $this->hack_id = $id; } public function print() { echo $this->hack_name.PHP_EOL; } public function __sleep() { return array("hack_name"); } public function __wakeup() { $this->hack_name = 'haha'; } } $obj = new fobnn('fobnn',1); $obj->print(); $serializedstr = serialize($obj); echo $serializedstr.PHP_EOL;; $toobj = unserialize($serializedstr); $toobj->print();
fobnn O:5:"fobnn":1:{s:16:"fobnnhack_name";s:5:"fobnn";} haha
在序列化以前会先调用__sleep
返回的是一个须要序列化的成员名称数组,经过这样咱们就能够控制须要序列化的数据,案例中我只返回了hack_name,能够看到结果中只序列化了hack_name成员.
在序列化完成以后,会跳用__wakeup
在这里咱们能够作一些后续工做,例如重连数据库之类的.
自定义序列化接口 http://php.net/manual/en/clas...
interface Serializable { abstract public string serialize ( void ) abstract public void unserialize ( string $serialized ) }
经过这个接口咱们能够自定义序列化和反序列化的行为,这个功能主要能够用来自定义咱们的序列化格式.
class fobnn implements Serializable { public $hack_id; private $hack_name; public function __construct($name,$id) { $this->hack_name = $name; $this->hack_id = $id; } public function print() { echo $this->hack_name.PHP_EOL; } public function __sleep() { return array('hack_name'); } public function __wakeup() { $this->hack_name = 'haha'; } public function serialize() { return json_encode(array('id' => $this->hack_id ,'name'=>$this->hack_name )); } public function unserialize($var) { $array = json_decode($var,true); $this->hack_name = $array['name']; $this->hack_id = $array['id']; } } $obj = new fobnn('fobnn',1); $obj->print(); $serializedstr = serialize($obj); echo $serializedstr.PHP_EOL;; $toobj = unserialize($serializedstr); $toobj->print();
fobnn C:5:"fobnn":23:{{"id":1,"name":"fobnn"}} fobnn
当使用了自定义序列化接口以后,咱们的魔术方法就没用了.
既然上文中提到的自描述功能,那么序列化结果中保存了对象的类型,且php是动态类型语言,那么咱们就能够来作个简单的实验.
class fobnn { public $hack_id; public $hack_name; public function __construct($name,$id) { $this->hack_name = $name; $this->hack_id = $id; } public function print() { var_dump($this->hack_name); } } $obj = new fobnn('fobnn',1); $obj->print(); $serializedstr = serialize($obj); echo $serializedstr.PHP_EOL;; $toobj = unserialize($serializedstr); $toobj->print(); $toobj2 = unserialize("O:5:\"fobnn\":2:{s:7:\"hack_id\";i:1;s:9:\"hack_name\";i:12345;}"); $toobj2->print();
咱们修改hack_name反序列化的结果为int类型, i:12345
string(5) "fobnn" O:5:"fobnn":2:{s:7:"hack_id";i:1;s:9:"hack_name";s:5:"fobnn";} string(5) "fobnn" int(12345)
能够发现,对象成功序列化回来了!而且能够正常工做!. 固然php的这种机制提供了灵活多变的语法,但也引入了安全风险. 后续继续分析php序列化和反序列化特性带来的安全问题.
最后 ending…若有不足请指点,亦可留言或联系 fobcrackgp@163.com.
本文为笃行原创文章首发于大题小做,永久连接:PHP反序列化漏洞系列之--PHP序列化和反序列化原理
https://www.ifobnn.com/phpserialize.html