当前位置:首页>php>写给自己之php反序列化-快递打包员的故事

写给自己之php反序列化-快递打包员的故事

  • 2026-10-11 05:53:17
写给自己之php反序列化-快递打包员的故事

第01章:快递打包员的故事——为什么计算机需要"序列化"?

系列化学习之php反序列化

今天聊一个听起来很唬人的词——"反序列化漏洞"。别被这四个字吓跑。

先别看代码。我们聊聊乐高。

你在淘宝上买了一套乐高千年隼,那个三千多块、7541块零件的变态套装。拼了一个月终于拼好了,摆在桌上威风凛凛。然后你想把它寄给成都的大学室友。

抱着千年隼去快递站,老板看了一眼:"兄弟你认真的?这玩意儿一米多长,快递盒都塞不下,路上颠两下零件全掉光。"

你想了想,也对。于是你把它拆了——按说明书一块一块拆开,塞进收纳袋。在袋子上贴张纸条:"千年隼号,75192 型号,7541 块零件,拼装说明请见乐高第 75192 号图纸。"然后塞进快递盒,寄走。

室友收到后,拆箱,看到收纳袋和纸条,翻出说明书,照着 7541 块零件的编号一块块拼回去——一小时后,千年隼又出现了。

这个"拆"和"拼",翻译成计算机术语,就是序列化和反序列化。

你拆乐高的时候做了两件事:把立体的模型压扁成可以装进盒子的零件,同时在收纳袋上贴了标签——这个标签很重要,没有它室友拿到一堆零件根本不知道这是什么、按什么图纸拼。

计算机序列化也是一样:把活的对象压成扁平化的字符串,同时在这串字符串里打上"标签"——类名、属性名、属性类型。对方拿到这串字符串,照着标签就能还原出对象。

你要是觉得乐高太玩具化了,换个更实在的:你玩《星露谷物语》,种了两百颗蓝莓。爽。该睡觉了,点"保存游戏"。

程序这时候在干嘛?

你那个农场、那个角色、那两百颗蓝莓——它们不是硬盘上某个文件里的静态数据,它们是"活"在内存里的程序对象。内存这东西有个毛病:一断电全清零。你辛辛苦苦种了一下午的蓝莓,关个电脑就全没了——不是因为游戏坏,是物理规律决定的。

所以程序得在关电脑之前,把这些还在内存里活蹦乱跳的对象"压扁"成一串能写到硬盘上的东西。压扁的动作就是序列化,写到硬盘就是存盘。

第二天打开电脑点"继续游戏"——程序从硬盘上把那串东西读回来,在内存里重新"充气",还原成那个有蓝莓的农场。的下午没有白费。

这就是序列化存在的全部意义。不是什么高端技术,就是一个朴素的物理问题:内存的东西会消失,得在它消失之前搬到硬盘上。而要搬家,就必须先把立体的东西压扁。

好了故事讲够了。写一行代码。

打开的 PHP,敲这个(手敲,不要复制粘贴,感受一下):

<?phpclassUser{public$name;public$age;}$user = newUser();$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,可能更熟悉:

echojson_encode($user);// {"name":"张三","age":18}

JSON 也是序列化,但它和 PHP 序列化有个根本区别:JSON 只能描述数据,不能描述"这是一个什么类型的对象"。json_decode 回来的是一个 stdClass,不是 User。PHP 序列化反序列化回来的是真正的 User 对象——这就是它比 JSON 强的地方,也是它比 JSON 危险的地方。因为真正的对象是有"行为"的——有方法、有魔术方法。后面几章会看到,这个区别是"能控制数据"和"能执行代码"之间的分水岭。

顺便说一句:序列化不是加密。O:4:"User":... 念出来就像念身份证号一样——人不加密,机器也不加密。base64 也不是加密,解码就回来了。加密需要密钥,序列化和 base64 都没有这玩意儿。

这一章记住三件事就行:

  1. 1. 序列化 = 把活对象压扁成字符串。反序列化 = 把字符串还原成活对象。
  2. 2. PHP 的序列化字符串是明文、没有任何保护。谁拿到都能看懂,都能改。
  3. 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()还原的完整过程。

最新文章

随机文章