写给自己之php反序列化-快递打包员的故事
原创
niuko
niuko
Ncko
2026年6月30日 09:30
安徽
在小说阅读器读本章
去阅读
第01章:快递打包员的故事——为什么计算机需要\"序列化\"?
系列化学习之php反序列化
今天聊一个听起来很唬人的词——\"反序列化漏洞\"。别被这四个字吓跑。
先别看代码。我们聊聊乐高。
你在淘宝上买了一套乐高千年隼,那个三千多块、7541块零件的变态套装。拼了一个月终于拼好了,摆在桌上威风凛凛。然后你想把它寄给成都的大学室友。
抱着千年隼去快递站,老板看了一眼:\"兄弟你认真的?这玩意儿一米多长,快递盒都塞不下,路上颠两下零件全掉光。\"
你想了想,也对。于是你把它拆了——按说明书一块一块拆开,塞进收纳袋。在袋子上贴张纸条:\"千年隼号,75192 型号,7541 块零件,拼装说明请见乐高第 75192 号图纸。\"然后塞进快递盒,寄走。
室友收到后,拆箱,看到收纳袋和纸条,翻出说明书,照着 7541 块零件的编号一块块拼回去——一小时后,千年隼又出现了。
这个\"拆\"和\"拼\",翻译成计算机术语,就是序列化和反序列化。
你拆乐高的时候做了两件事:把立体的模型压扁成可以装进盒子的零件,同时在收纳袋上贴了标签——这个标签很重要,没有它室友拿到一堆零件根本不知道这是什么、按什么图纸拼。
计算机序列化也是一样:把活的对象压成扁平化的字符串,同时在这串字符串里打上\"标签\"——类名、属性名、属性类型。对方拿到这串字符串,照着标签就能还原出对象。
你要是觉得乐高太玩具化了,换个更实在的:你玩《星露谷物语》,种了两百颗蓝莓。爽。该睡觉了,点\"保存游戏\"。
程序这时候在干嘛?
你那个农场、那个角色、那两百颗蓝莓——它们不是硬盘上某个文件里的静态数据,它们是\"活\"在内存里的程序对象。内存这东西有个毛病:一断电全清零。你辛辛苦苦种了一下午的蓝莓,关个电脑就全没了——不是因为游戏坏,是物理规律决定的。
所以程序得在关电脑之前,把这些还在内存里活蹦乱跳的对象\"压扁\"成一串能写到硬盘上的东西。压扁的动作就是序列化,写到硬盘就是存盘。
第二天打开电脑点\"继续游戏\"——程序从硬盘上把那串东西读回来,在内存里重新\"充气\",还原成那个有蓝莓的农场。的下午没有白费。
这就是序列化存在的全部意义。不是什么高端技术,就是一个朴素的物理问题:内存的东西会消失,得在它消失之前搬到硬盘上。而要搬家,就必须先把立体的东西压扁。
好了故事讲够了。写一行代码。
打开的 PHP,敲这个(手敲,不要复制粘贴,感受一下):
<?php
class User {
public $name;
public $age;
}
$user = new User();
$user->name = \"张三\";
$user->age = 18;
$packed = serialize($user);
echo $packed;
运行 php test.php,会看到一行鬼画符:
O:4:\"User\":2:{s:4:\"name\";s:6:\"张三\";s:3:\"age\";i:18;}
第一次看到这串东西正常人都会皱眉,但它没那么可怕。我带一个字一个字看:
O — Object 的首字母,\"注意了,接下来描述的是一个活对象\"
:4: — \"User\" 这个词有 4 个字母
\"User\" — 类名,这个对象的图纸编号
:2: — 有两个属性:name 和 age
{ } — 大括号里装着属性的具体内容
s:4:\"name\" — s = String,属性名是 name,4 个字母
s:6:\"张三\" — 两个中文字,UTF-8 下一个字 3 字节,两个字 6 字节
坑点提醒:新手最常在这里踩坑——不是 s:2,是 s:6。不信用
strlen(\"张三\")跑一下。
s:3:\"age\";i:18; — age 有 3 个字母,i = Integer,值是 18
搞清楚了?反过来试一下——拿到这串鬼画符,怎么还原对象:
$string = 'O:4:\"User\":2:{s:4:\"name\";s:6:\"李四\";s:3:\"age\";i:20;}';
$restored = unserialize($string);
echo $restored->name; // 李四
echo $restored->age; // 20
这就是反序列化——拿到\"快递盒\"(这串字符串),看标签(类名 + 属性描述),把对象还原出来。
PHP 的序列化还有个兄弟叫 JSON,可能更熟悉:
echo json_encode($user);
// {\"name\":\"张三\",\"age\":18}
JSON 也是序列化,但它和 PHP 序列化有个根本区别:JSON 只能描述数据,不能描述\"这是一个什么类型的对象\"。 json_decode 回来的是一个 stdClass,不是 User。PHP 序列化反序列化回来的是真正的 User 对象——这就是它比 JSON 强的地方,也是它比 JSON 危险的地方。因为真正的对象是有\"行为\"的——有方法、有魔术方法。后面几章会看到,这个区别是\"能控制数据\"和\"能执行代码\"之间的分水岭。
顺便说一句:序列化不是加密。O:4:\"User\":... 念出来就像念身份证号一样——人不加密,机器也不加密。base64 也不是加密,解码就回来了。加密需要密钥,序列化和 base64 都没有这玩意儿。
这一章记住三件事就行:
-
- 序列化 = 把活对象压扁成字符串。反序列化 = 把字符串还原成活对象。
-
- PHP 的序列化字符串是明文、没有任何保护。谁拿到都能看懂,都能改。
-
- PHP 序列化反序列化回来的对象是\"活的\"——有类、有方法、有可能会自动执行的魔术方法。这是它比 JSON 危险的根本原因。
下面几道作业,挑 2 到 3 个做就好:
- • 把上面的
serialize和unserialize各手敲跑一遍,改改$user->name看字符串哪里变了。 - • 把自己的名字(中文的话)设成
$user->name,看s:后面是多少。换成英文名再试,对比中英文字节数差异。 - • 同一个 User 对象,分别用
serialize和json_encode输出,对着两串字符串找差异。 - • 如果我胆子大:把序列化字符串里的
s:6:\"张三\"直接改成s:6:\"黑客\",反序列化看看能不能成功。想一下——如果序列化数据来自浏览器 Cookie,把 Cookie 里的\"张三\"改成了\"admin\",程序会怎么处理?
最后一个问题不是课后作业,是留给下一章的悬念。
ch01 代码运行演示
动图主要展示了从创建对象→serialize()拍扁→逐字节拆解→unserialize()还原的完整过程。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论