序列化
首先說說什麼是序列化
序列化給我們傳遞物件提供了一種簡單的方法。serialize()將一個物件轉換成一個字串,並且在轉換的過程中可以儲存當前變數的值
而反序列化unserialize()將字串還原為一個物件。
通俗來說:通過反序列化在特定條件下可以重建php物件並執行php物件中某些magic函式。
在PHP應用中,序列化和反序列化一般用做快取,比如session快取,cookie等。
舉一個簡單的例子
<?php
class people{
public $name;
public $age;
public $sex;
function __construct($name,$age,$sex){ //_construct:建立物件時初始化
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
}
$people=new people("hhy",20,"boy");
echo serialize($people);
?>
輸出結果:O:6:"people":3:{s:4:"name";s:3:"hhy";s:3:"age";i:20;s:3:"sex";s:3:"boy";}
“O”表示物件,6表示物件名長度為6
“people”為物件名,3表示有3個引數
“s”表示string物件
“i”表示int物件
反序列化輸出
$unpeople='O:6:"people":3:{s:4:"name";s:3:"hhy";s:3:"age";i:20;s:3:"sex";s:3:"boy";}'; var_dump(unserialize($unpeople)); //輸出用var_dump函式
或者 $u=unserialize('O:6:"people":3:{s:4:"name";s:3:"hhy";s:3:"age";i:20;s:3:"sex";s:3:"boy";}'); echo $u->name,$u->age,$u->sex;
輸出結果:object(people)#2 (3) { ["name"]=> string(3) "hhy" ["age"]=> int(20) ["sex"]=> string(3) "boy" }
輸出結果:hhy20boy
反序列化
序列化和反序列化本身沒有問題,但是如果反序列化的內容是使用者可以控制的,且後臺不正當的使用了PHP中的魔法函式,就會導致安全問題
- unserialize()函式的引數可控
- php中有可以利用的類並且類中有魔術方法
常見的魔術方法
__construct(): 在建立物件時候初始化物件,一般用於對變數賦初值。
__destruct(): 和建構函式相反,當物件所在函式呼叫完畢後執行。
__toString():當物件被當做一個字串使用時呼叫。
__sleep():序列化物件之前就呼叫此方法(其返回需要一個陣列)
__wakeup():反序列化恢復物件之前呼叫該方法
__call():當呼叫物件中不存在的方法會自動呼叫該方法。
__get():在呼叫私有屬性的時候會自動執行
__isset()在不可訪問的屬性上呼叫isset()或empty()觸發
__unset()在不可訪問的屬性上使用unset()時觸發
<head>
<meta charset="UTF-8">
</head>
<?php
class T{
public $test=1;
function __construct(){
echo '呼叫了_construct<br>';
}
function __destruct(){
echo '呼叫了_destruct<br>';
}
//function __sleep(){
// echo '呼叫了_sleep<br>'
//}
function __wakeup(){
echo '呼叫了_wakeup<br>';
}
}
$t=new T();
echo $t->test;
echo "<br/>";
$t1=serialize($t);
echo $t1;
echo "<br/>";
$t2=unserialize($t1);
echo $t->test;
echo "<br/>";
當程式執行前,serialize() 函式會首先檢查是否存在一個魔術方法__sleep.如果存在,__sleep()方法會先被呼叫,然後才執行序列化操作。這個功能可以用於清理物件,並返回一個包含物件中所有變數名稱的陣列。如果該方法不返回任何內容,則NULL被序列化,導致一個E_NOTICE錯誤。
unserialize()會檢查是否存在一個__wakeup方法。如果存在,則會先呼叫 __wakeup方法,預先準備物件資料。
漏洞舉例:
class S{
var $test = "pikachu";
function __destruct(){
echo $this->test;
}
}
$s = $_GET['test'];
@$unser = unserialize($a);
payload:O:1:"S":1:{s:4:"test";s:29:"<script>alert('xss')</script>";}
防禦
反序列化的問題是使用者引數的控制問題引起的,所以好的預防措施就是不要把使用者的輸入或者是使用者可控的引數直接放進反序列化的操作中去。