unserialize不安全,因会触发魔术方法导致rce;仅限可信内部数据使用,禁用类或白名单限制,存储前加hmac签名校验。

PHP 的 serialize 和 unserialize 不是“安全的通用序列化方案”,能不用就别用,尤其不能直接反序列化用户输入。
为什么 unserialize 一碰就出问题
它不是解析数据,而是执行 PHP 对象重建逻辑——只要类定义存在,__wakeup、__destruct 等魔术方法就会被触发。攻击者构造恶意字符串就能远程执行代码。
- 常见错误现象:
unserialize()报错Notice: unserialize(): Error at offset ...,或更糟:服务被黑 - 典型使用场景:缓存对象、Session 存储、旧系统接口传参(现在应避免)
- 根本原因:
serialize输出含类名、属性名、长度等原始结构信息,无法校验来源和完整性
serialize 的实际限制和替代选择
它只支持 PHP 原生类型和已声明的类,且结果不可跨语言读取,也不压缩体积。
-
serialize后的字符串含 PHP 版本敏感信息(如对象属性顺序),PHP 7.4 和 8.1 序列化同一对象可能不兼容 - 数组键为浮点数时会被转成整数:
serialize(['1.5' => 'a'])实际存的是['1' => 'a'] - 替代方案优先级:
json_encode/json_decode(无对象、无资源、需 UTF-8) →igbinary_serialize(扩展,二进制、跨版本稍稳) → 自定义扁平化逻辑
如果非用不可,怎么降低风险
核心原则:只对完全可控、来源可信、结构固定的内部数据使用,且绝不让 unserialize 接触任何外部输入。
立即学习“PHP免费学习笔记(深入)”;
- 用
unserialize($data, ['allowed_classes' => false])禁用所有类(PHP 7.0+),强制只还原数组/字符串/数字等基础类型 - 若必须支持特定类,显式列出:
['allowed_classes' => ['MyCacheItem', 'ConfigBag']],禁止true或空数组 - 存储前加签名:用
hash_hmac('sha256', $serialized, $secret_key)生成校验值,反序列化前先比对 - 不要把
serialize结果直接写进日志或数据库字段——容易混入不可见控制字符,导致后续unserialize崩溃
真正麻烦的不是语法怎么写,而是你得确认整个调用链里没有一个地方把用户 POST 的 $_POST['data'] 直接喂给了 unserialize。这种漏洞一旦存在,修复成本远高于改用 JSON。











