unserialize() 是真正的风险点,因它会重建对象并自动触发 __wakeup()、__destruct() 等魔术方法;若处理用户可控输入,攻击者可操控执行上下文,导致远程代码执行或文件写入。

直接用 serialize() 本身没有安全隐患,危险的是后续的 unserialize() ——尤其当它处理用户可控输入时。
为什么 unserialize() 是真正的风险点
序列化只是把对象“拍扁”成字符串,是纯数据操作;反序列化却会重建对象、恢复属性,并自动触发 __wakeup()、__destruct() 等魔术方法。一旦攻击者能控制传入 unserialize() 的字符串,就等于能控制这些方法的执行上下文。
- 常见错误场景:从
$_COOKIE、$_GET['data']、Redis 缓存值、日志字段中直接取值反序列化 - 典型危害链:
unserialize($user_input)→ 触发恶意类的__destruct()→ 调用system($this->cmd)或file_put_contents($this->file, $this->content) - 即使你没写危险类,第三方库或旧框架里可能已存在可利用的 gadget(比如
Monolog、SwiftMailer历史版本)
allowed_classes 参数不是万能解药
PHP 7.0+ 支持 unserialize($str, ['allowed_classes' => ['SafeClass']]),但它只限制类名白名单,不校验属性值是否被篡改。
- 如果
SafeClass自身有__destruct()且用了$this->logFile,攻击者仍可把logFile设为/var/www/shell.php -
allowed_classes => false可禁用所有类(只允许数组/字符串等基础类型),但很多业务逻辑依赖对象,强行设为false会导致功能崩溃 - 该参数对
phar://协议绕过无效——攻击者把恶意 payload 打包进.phar文件,再用file_get_contents('phar://evil.phar')触发反序列化,完全绕过unserialize()调用
真正有效的防护策略组合
单点防御大概率失效,必须分层拦截:
- 根本原则:永远不要对用户输入调用
unserialize()。优先改用json_encode()/json_decode()——它们只处理数组、字符串、数字等基础结构,不重建对象、不触发魔术方法 - 若必须保留对象序列化(如缓存复杂对象),改用
igbinary或msgpack等二进制格式,并确保反序列化端严格校验数据结构(比如用assert(is_array($data) && isset($data['type']))) - 在入口层加 RASP 防护(如阿里云应用防护、OpenRASP),它能在运行时 Hook
unserialize()调用,检测参数是否来自$_GET或$_COOKIE并实时阻断 - 代码层加固:所有含魔术方法的类,必须对关键属性做白名单校验(例如
__destruct()中检查$this->logFile是否在['/tmp/log.txt', '/var/log/app.log']内)
最常被忽略的一点:开发者总盯着“怎么安全地反序列化”,却忘了问“这里真的需要反序列化吗”。多数业务场景下,用 JSON 替代 + 显式构造对象,比修补 unserialize() 的漏洞更简单、更可靠。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











