PHP反序列化如何规避

wen 开源项目 27

本文目录导读:

PHP反序列化如何规避

  1. 目录导读
  2. 反序列化漏洞的本质
  3. 常见攻击手法与示例
  4. 七条核心规避策略
  5. 工具与实战演练
  6. 常见QA问答

PHP反序列化漏洞规避指南:从原理到实战的防御策略

目录导读

  1. 反序列化漏洞的本质

    • 序列化与反序列化的基础概念
    • 为什么反序列化会成为攻击入口?
  2. 常见攻击手法与示例

    • wakeup() / destruct() 魔术方法滥用
    • 利用POP链(面向属性编程)实现远程代码执行
  3. 七条核心规避策略

    • 策略1:严格校验输入来源
    • 策略2:使用白名单类机制
    • 策略3:禁用危险魔术方法
    • 策略4:启用字符串校验与哈希
    • 策略5:采用现代序列化格式(JSON/MessagePack)
    • 策略6:分离反序列化权限与执行环境
    • 策略7:定期审计与动态监控
  4. 工具与实战演练

    • PHP内置安全函数列表
    • 开源工具(如PhpSecInfo、RIPS)配置建议
  5. 常见QA问答

    • Q:反序列化是否只有PHP有风险?
    • Q:只通过过滤能否完全规避?
    • Q:如何验证当前代码是否存在漏洞?

反序列化漏洞的本质

序列化与反序列化的基础概念

在PHP中,serialize()将对象转化为可存储的字符串,unserialize()则逆向还原。

class User {
    public $name = 'admin';
    public $isAdmin = false;
}
$serialized = serialize(new User());
// 输出:O:4:"User":2:{s:4:"name";s:5:"admin";s:7:"isAdmin";b:0;}

该机制常用于缓存、Session存储、API数据交换,但当用户可控数据被直接反序列化时,漏洞便诞生

为什么反序列化会成为攻击入口?

因为反序列化过程中,PHP会自动触发对象的魔术方法(如__wakeup()__destruct()__toString()),攻击者可通过构造特殊序列化数据,迫使这些方法执行恶意逻辑(如写入WebShell、执行系统命令)。

关键点:漏洞不在序列化本身,而在反序列化后对未过滤的用户数据进行了不安全操作。


常见攻击手法与示例

wakeup() / destruct() 魔术方法滥用

假设某类包含如下控制器:

class Hook {
    public $cmd;
    public function __destruct() {
        system($this->cmd);
    }
}

攻击者构造序列化数据(通过修改$cmd为任意命令),调用unserialize()即可触发__destruct()执行。

利用POP链(面向属性编程)实现远程代码执行

当单一类无法提供直接危险方法时,攻击者会串联多个类(Gadget Chains),例如利用YiiThinkPHP等框架的内置类组合攻击,典型如CVE-2018-5711(ThinkPHP5反序列化RCE)。

攻击流程

  1. 寻找实现__toString()的类 → 调用文件包含函数 → 最终执行恶意脚本。

七条核心规避策略

策略1:严格校验输入来源

绝对不要直接unserialize($_GET['data'])
如果必须接受外部数据,应先检查来源(如签名、Token),并限制仅可反序列化已签名数据,示例:

$data = $_POST['data'];
$sign = $_POST['sign'];
if (md5($data . SECRET_KEY) !== $sign) {
    die('数据被篡改!');
}
$obj = unserialize($data); // 仅在签名校验通过后执行

策略2:使用白名单类机制

PHP 7.0+允许在unserialize()第二个参数设置允许的类列表:

$allowed = ['SafeClass1', 'SafeClass2'];
$obj = unserialize($data, ['allowed_classes' => $allowed]);

$allowed设为false,则仅反序列化简单类型(如数组、整数),彻底杜绝类操作漏洞。

策略3:禁用危险魔术方法

在自定义类中,慎用__wakeup()__destruct()__toString(),若必须使用,确保内部不调用敏感函数(evalsystemfile_put_contents等),可重写__wakeup()为空方法降低风险。

策略4:启用字符串校验与哈希

为序列化字符串添加HMAC签名:

$data = serialize($obj);
$sig = hash_hmac('sha256', $data, SECRET_KEY);
$final = base64_encode($sig . '|' . $data);
// 反序列化前
list($sig, $data) = explode('|', base64_decode($input));
if (!hash_equals(hash_hmac('sha256', $data, SECRET_KEY), $sig)) {
    die('校验失败');
}
$obj = unserialize($data);

注意:密钥需妥善保管(如放在环境变量中)。

策略5:采用现代序列化格式(JSON/MessagePack)

若业务场景允许,使用json_encode/json_decode代替serialize/unserialize,JSON不包含类信息,无法触发魔术方法。
注意:对于复杂嵌套对象,可配合json_decode($data, true)转为数组处理。

策略6:分离反序列化权限与执行环境

  • 在独立的低权限容器(Docker)中执行反序列化操作。
  • 禁用危险函数:在php.ini中设置disable_functions=system,exec,passthru,shell_exec,proc_open
  • 使用open_basedir限制文件读写路径。

策略7:定期审计与动态监控

  • 使用静态分析工具(如RIPS、PhpParser)扫描反序列化入口。
  • 在日志中记录unserialize()调用的参数与结果,异常时触发告警。
  • 部署WAF规则屏蔽序列化字符串特征(如O:\d+:")。
    注意:WAF不能替代代码层防御,但可作为辅助层。

工具与实战演练

PHP内置安全函数列表

  • filter_var($data, FILTER_UNSAFE_RAW):用于验证输入格式。
  • is_serialized()(自定义函数):检查字符串是否符合序列化格式,但不推荐作为唯一验证。

开源工具配置建议

  • RIPS:扫描PHP代码中不安全的unserialize()调用,并生成POP链分析报告。
  • PhpSecInfo:检查php.iniunserialize_max_classes等参数配置。
  • Deva:运行时监控反序列化过程,支持阻断高危类加载。

实战演练(模拟防御)

场景:系统使用unserialize()还原Session数据。

规避实施

  1. 将Session存储改为Redis,使用session.serialize_handler=php_serialize时限制允许类为['User']
  2. User类中添加__wakeup()检查成员变量(如$isAdmin是否被篡改):
class User {
    private $id;
    private $role;
    public function __wakeup() {
        if ($this->role === 'admin' && $_SERVER['REMOTE_ADDR'] !== '127.0.0.1') {
            $this->role = 'user'; // 强制降权
        }
    }
}

常见QA问答

Q:反序列化是否只有PHP有风险?

不是,Python(pickle)、Java(ObjectInputStream)、Ruby(Marshal)均存在类似漏洞,但PHP的“魔术方法”机制和弱类型特性使其高危场景更易触发。

Q:只通过过滤能否完全规避?

不能,因为攻击者可通过编码变形(如gzip压缩序列化字符串)、分块提交等方式绕过关键词过滤。安全的关键在于“从不信任输入”,而非过滤。

Q:如何验证当前代码是否存在漏洞?

  1. 搜索代码中所有unserialize()调用,检查其参数是否源自用户输入(GET/POST/Cookie/文件上传)。
  2. 检测当前PHP版本是否低于7.3(官方对unserialize()allowed_classes参数修复)。
  3. 使用php -r 'print_r(unserialize($_GET["x"]));'这类临时测试端点并观察错误响应。

记住:反序列化防御不能依赖单一方法,必须组合策略1~7形成纵深防御体系,即使代码经过审计,也应定期审查第三方库中新增的POP链(例如Composer管理的依赖包)。


本文参考了OWASP反序列化防护清单、PHP官方安全手册以及CVE漏洞报告中的实战案例,内容经去伪原创处理,符合当前主流防御最佳实践。

抱歉,评论功能暂时关闭!