🔐 反序列化为什么危险:对象不是数据,代码也会跟着进来
很多人第一次接触反序列化漏洞,会觉得它只是“把字符串还原成对象”而已,听起来像普通的数据解析。但危险就在这里:如果一种序列化格式不只是保存数据,还保存“对象类型”和“构造方式”,那程序在反序列化时就可能顺手执行一串你没打算执行的逻辑。Java 的
为什么会发生?因为开发者脑子里常有个错觉:来自数据库、缓存、消息队列、Cookie、文件上传里的内容,看起来像内部格式,就默认安全。可攻击者只要能控制这段序列化数据,就能伪造一个对象图,让程序在恢复对象时自动调用魔术方法、钩子函数或者 gadget chain。于是反序列化不再是“读数据”,而变成了“按攻击者指定的方式跑代码”。现实里常见后果包括远程命令执行、任意文件读写、权限绕过,甚至只是一个签名校验缺口,也足够让攻击者把本地组件串成武器。
真正实用的防法也不是“多加几个黑名单类名”。黑名单是补丁思维,迟早漏。更靠谱的做法是别用会恢复任意对象的格式处理外部输入,能用 JSON、MessagePack 这种纯数据格式,就别把类实例直接扔出去再捡回来。如果历史包袱太重,至少要做三件事:第一,反序列化前做严格完整性校验,比如带密钥的签名,而不是裸 base64;第二,限制可反序列化的类型白名单;第三,把危险逻辑从对象的构造、析构、魔术方法里拆出去,别让“恢复对象”顺手触发副作用。说白了,输入应该只是数据,不该拥有决定程序控制流的权力。
🧪 你在审计一个老 Web 系统,发现它把登录态存在 Cookie 里,值是 base64 后的 PHP serialize 字符串。开发者说“攻击者看不懂内容,而且 Cookie 里没有命令执行代码,所以问题不大”。这里最关键的风险点是什么?
💡 核心记忆:只要外部输入能被还原成“对象”,你就得假设它可能把程序控制流一起带进来。
#Security
很多人第一次接触反序列化漏洞,会觉得它只是“把字符串还原成对象”而已,听起来像普通的数据解析。但危险就在这里:如果一种序列化格式不只是保存数据,还保存“对象类型”和“构造方式”,那程序在反序列化时就可能顺手执行一串你没打算执行的逻辑。Java 的
readObject、Python 的 pickle、PHP 的 unserialize 都出过这类问题,本质不是库“脆弱”,而是你把“不可信输入”当成了“可信对象”。为什么会发生?因为开发者脑子里常有个错觉:来自数据库、缓存、消息队列、Cookie、文件上传里的内容,看起来像内部格式,就默认安全。可攻击者只要能控制这段序列化数据,就能伪造一个对象图,让程序在恢复对象时自动调用魔术方法、钩子函数或者 gadget chain。于是反序列化不再是“读数据”,而变成了“按攻击者指定的方式跑代码”。现实里常见后果包括远程命令执行、任意文件读写、权限绕过,甚至只是一个签名校验缺口,也足够让攻击者把本地组件串成武器。
真正实用的防法也不是“多加几个黑名单类名”。黑名单是补丁思维,迟早漏。更靠谱的做法是别用会恢复任意对象的格式处理外部输入,能用 JSON、MessagePack 这种纯数据格式,就别把类实例直接扔出去再捡回来。如果历史包袱太重,至少要做三件事:第一,反序列化前做严格完整性校验,比如带密钥的签名,而不是裸 base64;第二,限制可反序列化的类型白名单;第三,把危险逻辑从对象的构造、析构、魔术方法里拆出去,别让“恢复对象”顺手触发副作用。说白了,输入应该只是数据,不该拥有决定程序控制流的权力。
🧪 你在审计一个老 Web 系统,发现它把登录态存在 Cookie 里,值是 base64 后的 PHP serialize 字符串。开发者说“攻击者看不懂内容,而且 Cookie 里没有命令执行代码,所以问题不大”。这里最关键的风险点是什么?
unserialize()💡 核心记忆:只要外部输入能被还原成“对象”,你就得假设它可能把程序控制流一起带进来。
#Security