<?php // 定义一个用户类 class User { public $name; // 魔术方法:对象被销毁时执行 public function __destruct() { // 执行$name里的内容(危险!直接执行用户输入) eval($this->name); } } // 接收用户传入的序列化字符串(无过滤) $user_input = $_GET['data']; // 反序列化(触发漏洞) unserialize($user_input); ?> |
攻击者需要构造一个序列化字符串,让$name 变成恶意代码(比如读取服务器密码文件):<?php class User { public $name = "system('cat /etc/passwd');"; // 读取密码文件 } // 序列化这个对象,得到攻击字符串 echo serialize(new User()); ?> |
http://xxx.com/vuln.php?data=O:4:"User":1:{s:4:"name";s:26:"system('cat /etc/passwd');";}程序反序列化后,会触发__destruct() 方法,执行system('cat /etc/passwd'),攻击者就能拿到服务器密码文件。记住核心原则:不要信任任何外部输入,尤其是反序列化的数据,具体做法分 5 点:
1. 避免直接反序列化不可信数据
这是最根本的办法!如果不是必须,不要让用户传入序列化字符串。如果必须用,优先用更安全的格式(比如 JSON)替代。
2. 过滤 + 验证输入
对传入的序列化字符串进行过滤,禁止包含eval、system、exec等危险函数;
验证序列化字符串的格式(比如长度、类名),只允许指定的类被反序列化(比如只允许User类,拒绝其他未知类)。
3. 禁用危险魔术方法
如果不需要,尽量避免使用__destruct()、__wakeup()、__call()等魔术方法,或者在这些方法里不要执行用户可控的代码。
4. 限制 PHP 函数执行权限
通过php.ini配置disable_functions,禁用eval、system、exec、passthru等危险函数:
disable_functions = eval,system,exec,passthru,shell_exec
5. 升级 PHP 版本
PHP 官方会修复已知的反序列化漏洞,及时升级到最新稳定版(比如 PHP 7.4+、PHP 8.x),可以避免旧版本的漏洞被利用。
PHP 反序列化漏洞的本质是 “信任了不可信的输入,并且执行了恶意代码”。对于开发者来说,核心是 “不直接执行用户可控的代码”;对于普通读者,了解这个漏洞后,也能明白 “为什么有些网站不让你随便上传文件 / 输入特殊字符”—— 都是为了防范类似的攻击。如果你的网站用了 PHP,赶紧检查一下有没有直接反序列化用户输入的代码,按照上面的方法加固吧!