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

admin 2026-10-21 23:36:00 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

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


    写给自己之php反序列化-快递打包员的故事" 网络安全文章

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

    本文通过乐高拆装和游戏存档的生动比喻,系统讲解PHP序列化与反序列化的核心概念。重点解析了serialize()生成的字符串结构(如O:4:\\`User\\`表示4字母类名User),对比JSON强调PHP反序列化会还原真实对象(含魔术方法)的安全风险。提供可操作代码实验:修改序列化字符串中的姓名值,体验反序列化过程并思考Cookie数据篡改可能带来的安全隐患。
    中孚信息发布《中孚信息2026年6月网络安全月报》" 网络安全文章

    中孚信息发布《中孚信息2026年6月网络安全月报》"

    中孚信息2026年6月网络安全月报分析本月全球APT攻击活动、数据泄露事件及高危漏洞,指出地缘政治冲突仍是APT主要攻击背景,互联网行业数据泄露严重,个人隐私为重点目标,并列出ApacheActiveMQ、Azure等关键漏洞。报告为网络安全规划提供决策依据,本月威胁等级评定为中。
    针对RHEL10上IPV6_FRAG_ESCAPE漏洞的公开PoC与利用细节已发布" 网络安全文章

    针对RHEL10上IPV6_FRAG_ESCAPE漏洞的公开PoC与利用细节已发布"

    安全研究员MassimilianoOldani公开了IPV6_FRAG_ESCAPE漏洞的PoC利用细节,该漏洞针对CentOS和RHEL10系统的Linux内核IPv6分片机制,可通过容器内非特权进程实现容器逃逸获取宿主机root权限。攻击利用\\_\\_ip6_append_data()函数的slab线性溢出破坏skb_shared_info结构,结合用户命名空间实现权限提升。建议立即升级至已修复内核(提交736b380e28d0)或通过设置user.max_user_namespaces=0
    评论:0   参与:  0