文章总结: 本文通过乐高拆装和游戏存档的生动比喻,系统讲解PHP序列化与反序列化的核心概念。重点解析了serialize()生成的字符串结构(如O:4:`User`表示4字母类名User),对比JSON强调PHP反序列化会还原真实对象(含魔术方法)的安全风险。提供可操作代码实验:修改序列化字符串中的姓名值,体验反序列化过程并思考Cookie数据篡改可能带来的安全隐患。 综合评分: 85 文章分类: WEB安全,代码审计,安全开发,漏洞分析,安全意识
写给自己之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 都没有这玩意儿。
这一章记住三件事就行:
- 1. 序列化 = 把活对象压扁成字符串。反序列化 = 把字符串还原成活对象。
- 2. PHP 的序列化字符串是明文、没有任何保护。谁拿到都能看懂,都能改。
- 3. 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()还原的完整过程。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:Ncko niuko niuko《写给自己之php反序列化-快递打包员的故事》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论