序列化与反序列化
utf8和UTF-8的区别
在使用中常常遇到 utf-8 和 utf8,现在终于弄明白他们的使用不同之处了,现在来和大家分享一下,下面我们看一下 utf-8 和 utf8 有什么区别。
“UTF-8” 是标准写法,php 在 Windows 下边英文不区分大小写,所以也可以写成 “utf-8”。“UTF-8” 也可以把中间的"-“省略,写成 “UTF8”。一般程序都能识别,但也有例外(如下文),为了严格一点,最好用标准的大写"UTF-8”。
在数据库中只能使用"utf8"(MySQL) 在MySQL的命令模式中只能使用"utf8",不能使用"utf-8",也就是说在PHP程序中只能使用 “set names utf8(不加小横杠)”,如果你加了"-“此行命令将不会生效,但是在 PHP 中 header 时却要加上”-“,因为 IE 不认识没杠的"utf8”,原因见下文。
PHP 中的 header:
<?php header('Content-Type: text/html; charset=UTF-8'); ?> //奇怪了:Content-Type 用冒号,Chatset却是等号。
静态文件使用:
总结:【只有在MySQL中可以使用"utf-8"的别名"utf8",但是在其他地方一律使用大写"UTF-8"。】
具体为:
除了在命令 “mysql_query(set names utf8)” 外一律用大写"UTF-8"。
**只要是符合序列化规则的字符串,自己构造一个也是可以被反序列化的。**因此我们可以构造带有攻击性的序列化字符串来利用漏洞。
一、序列化
将对象转换为字符串:便于存储,便于传输
如果要序列化一个类的实例,则最好在 __sleep() 魔术方法中明确指定要序列化的类属性,序列化的过程质包含类属性,不包含类方法
数组的序列化
- 网络上传输过程中,就比如 不可能传输一个数组到目标位置,但是我们可以以字符串的形式传输,于是便有了序列化(比如,JSON序列化,这个是JS处能够识别的序列化)
$student = array("name"=>"杨明强","age"=>"19","addr"=>"陕西汉中","phone"=>"13845678910");
// 使用 serialize 序列化函数,将 $student 转换为字符串
echo serialize($student);
输出结果:
a:4:{s:4:"name";s:9:"杨明强";s:3:"age";s:2:"19";s:4:"addr";s:12:"陕西汉中";s:5:"phone";s:11:"13845678910";}
a - 代表数组
s - 代表字符串
数字 - 代表该数组的元素个数或该字符串的长度
对象的序列化
/**针对实例的序列化与反序列化 */
$db1 = new DB();
echo serialize($db1)."<br>";
输出结果
O:2:"DB":5:{s:8:"DBhost";s:9:"localhost";s:12:"DBusername";s:4:"root";s:12:"DBpassword";s:9:"p-0p-0p-0";s:12:"DBdatabase";s:9:"woniunote";s:8:"DBconn";O:6:"mysqli":19:{s:13:"affected_rows";N;s:11:"client_info";N;s:14:"client_version";N;s:13:"connect_errno";N;s:13:"connect_error";N;s:5:"errno";N;s:5:"error";N;s:10:"error_list";N;s:11:"field_count";N;s:9:"host_info";N;s:4:"info";N;s:9:"insert_id";N;s:11:"server_info";N;s:14:"server_version";N;s:4:"stat";N;s:8:"sqlstate";N;s:16:"protocol_version";N;s:9:"thread_id";N;s:13:"warning_count";N;}}
-
O - 代表object对象的意思,是哪个类的对象呢,DB类的
-
O:2:"DB":5:
中的5代表该对象有5个属性 -
DBhost DBusername DBpassword DBdatabase DBconn
这就是该对象的5个属性,但事实上,属性的名字是没有前面DB两个字的,序列化之后存在DB的原因是因为,这些属性是private私有的,故而加上了DB不仅如此,序列化为了区分类名和类属性名,在DB后还加上了分隔符 00 ,所以序列化之后的私有类属性的值实际上是
DB00host
,但是00我们是看不见的。 -
也正是因为如此,所以
s:8:"DBhost";
显示该字符串的长度为8,实际上属性名为 host ,由于该属性为类的私有属性,便有了DBhost,为了区分类名和属性名,序列化又加上了分隔符 00 ,于是长度便成为了8 -
s:8:"DBconn";O:6:"mysqli":19:
表示DBconn
该私有属性的值也是一个对象,是 mysqli 类的对象,其值有19个。也正是因为他也是对象,故序列化后的字符串结尾会有两个 } 。这是对象中的属性也是一个对象,就类似于数组中存了一个数组的道理差不多
二、反序列化
数组反序列化
把一个字符串转换为一个对象
$source = 'a:4:{s:4:"name";s:9:"杨明强";s:3:"age";s:2:"19";s:4:"addr";s:12:"陕西汉中";s:5:"phone";s:11:"13845678910";}';
$student = unserialize($source);
print_r($student);
输出结果:
Array ( [name] => 杨明强 [age] => 19 [addr] => 陕西汉中 [phone] => 13845678910 )
对象反序列化
$source = 'O:2:"DB":4:{s:4:"host";s:9:"127.0.0.1";s:8:"username";s:4:"root";s:8:"password";s:9:"p-0p-0p-0";s:8:"database";s:9:"woniunote";}';
$db2 = unserialize($source);
echo $db2->host."<br>";
$rows = $db2->query("select articleid,headline from article where articleid<11;");
print_r($rows);
echo "<br>";
输出结果
127.0.0.1
Warning: mysqli_query() expects parameter 1 to be mysqli, null given in D:\WEB\XAMPP5.6\htdocs\learn\PHP\commoncondb.php on line 48
Warning: mysqli_fetch_all() expects parameter 1 to be mysqli_result, null given in D:\WEB\XAMPP5.6\htdocs\learn\PHP\commoncondb.php on line 49
调用析构函数,关闭数据库连接
Warning: mysqli_close() expects parameter 1 to be mysqli, null given in D:\WEB\XAMPP5.6\htdocs\learn\PHP\commoncondb.php on line 81
说明什么,字符串反序列化为对象成功,并成功调用类的public属性输出,且操作完之后会自动调用析构函数。但是析构函数调用失败,原因是因为,反序列化构造的对象并没有通过构造函数来构造对象,连接对象根本没有建立,因此调用析构函数关闭连接时,无从析构,便报错了
且这种情况因为没有建立连接对象,因此也就无法进行 query 或 update (针对这种情况需要使用魔术方法 __wakeup())
function __wakeup()
{
echo "DB类正在被反序列化: <br>";
// 因为类实例被反序列化之后,属性都是有值的,因此直接创建连接即可
$this->connect_db();
}
此时我们在调用上述反序列化代码,并执行查询看看效果
DB类正在被反序列化:
127.0.0.1
Array ( [0] => Array ( [articleid] => 1 [headline] => 漫谈:强哥在强哥学堂想对朋友们说的一些话 ) [1] => Array ( [articleid] => 2 [headline] => 漫谈:已经有蜗牛学院了,为什么还要创办强哥学堂? ) [2] => Array ( [articleid] => 3 [headline] => 漫谈:软件开发和软件测试,我该如何选择? ) [3] => Array ( [articleid] => 4 [headline] => 漫谈:Java和Python现在都挺火,我应该怎么选? ) [4] => Array ( [articleid] => 5 [headline] => 漫谈:强哥关于程序设计学习和实战经验分享 ) [5] => Array ( [articleid] => 6 [headline] => 管理:强哥关于人才培养方面的实战经验分享 ) [6] => Array ( [articleid] => 7 [headline] => 管理:强哥关于团队建设方面的实战经验分享 ) [7] => Array ( [articleid] => 8 [headline] => 漫谈:强哥关于软件测试学习和实战经验分享 ) [8] => Array ( [articleid] => 9 [headline] => 会员福利姬到底是个什么鬼? ) [9] => Array ( [articleid] => 10 [headline] => 管理:项目管理十大TION法 ) )
调用析构函数,关闭数据库连接
ok,问题被解决
三、魔术方法
序列化时自动调用 sleep() 休眠这些字符串,保存状态,然后 反序列化时 自动调用 wakeup
将之前保存或字符串的状态取出来,用来唤醒或恢复状态
/**针对实例的序列化与反序列化 */
$db1 = new DB();
echo serialize($db1)."<br>";
基于 一 中的对象序列化结果有如下处理:
在本节使用的数据库类的条件下,序列化类实例对象时,因为只要前四个属性正确了,那么连接对象通过构造函数自然而然就能创建成功, 于是我们只是想序列化实例对象 $db1
,但并不想序列化连接对象 $conn,这里我们就需要使用魔术方法 __sleep() 了
(1)__sleep() :
休眠:相当于暂停,用于保存当前状态
在类进行序列化时自动调用,并在内部需要明确指定和定义序列化时哪些属性,且是以数组形式定义和返回。
魔术方法是在类中的方法使用
function __sleep()
{
echo "DB类正在序列化: <br>";
return array('host','username','password','database');
}
然后现在我们来看看结果
O:2:"DB":4:{s:8:"DBhost";s:9:"localhost";s:12:"DBusername";s:4:"root";s:12:"DBpassword";s:9:"p-0p-0p-0";s:12:"DBdatabase";s:9:"woniunote";}
现在的结果就只序列化了类的前四个属性,而没有序列化 $conn 这个mysqli类对象
(2)__wakeup()
唤醒 在类进行反序列化时,便自动调用,并且可以在该方法中定义恢复类状态的代码,以便于让反序列化的实例可以正常调用类方法,就有点像 构造方法
function __wakeup()
{
echo "DB类正在被反序列化: <br>";
// 因为类实例被反序列化之后,属性都是有值的,因此直接创建连接即可
$this->connect_db();
}
四、反序列化漏洞:
- 字符串 反序列化为 对象的过程中,如果没有对字符串进行输入检查,很有可能被注入恶意代码
- 通常情况下,反序列化漏洞并非通过黑盒测试或盲注的方式来进行探测,而是通过代码评审惊醒漏洞验证(针对内部系统和开源系统)。