反序列化漏洞:数据对象的致命陷阱

作者:Yolo 发布时间: 2026-05-27 阅读量:13

反序列化漏洞:数据对象的致命陷阱

在现代软件开发中,序列化是将对象转换为可存储或传输格式(如 JSON、XML、二进制)的过程,反序列化则是将这些数据还原为对象。这个看似简单的操作,却隐藏着巨大的安全风险——反序列化漏洞

一、什么是反序列化漏洞

反序列化漏洞发生在应用程序将不可信数据反序列化为对象时,攻击者通过构造恶意序列化数据,在反序列化过程中执行任意代码、篡改对象属性或触发非预期的逻辑。

核心问题

反序列化操作本质上是在"重建对象",如果程序在重建过程中调用了危险的方法(如构造函数、__wakeupreadObject 等),攻击者就能控制执行流程。

二、漏洞原理详解

1. PHP 反序列化

PHP 使用 serialize()unserialize() 进行序列化操作。当 unserialize() 处理恶意数据时,会触发对象的 __wakeup()__destruct() 方法。

class EvilClass {
public $cmd;

function __destruct() {
// 危险!对象销毁时执行系统命令
system($this->cmd);
}
}

// 恶意序列化数据
$payload = 'O:8:"EvilClass":1:{s:3:"cmd";s:6:"whoami";}';
$obj = unserialize($payload); // 触发 __destruct,执行 whoami

2. Java 反序列化

Java 的 ObjectInputStream.readObject() 是重灾区。许多库(如 Apache Commons Collections)的类在反序列化时会执行危险操作。

// 恶意序列化流通过 readObject 触发命令执行
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object obj = ois.readObject(); // 危险!可能执行任意代码

3. Python Pickle 反序列化

Python 的 pickle 模块同样危险,__reduce__ 方法允许在反序列化时执行任意代码。

import pickle
import os

# 构造恶意 pickle 数据
class Evil:
def __reduce__(self):
return (os.system, ('whoami',))

payload = pickle.dumps(Evil())
pickle.loads(payload) // 执行 whoami

三、常见攻击场景

1. 远程代码执行(RCE)

最直接的后果。攻击者通过构造特定的序列化数据,在服务器上执行任意命令。

典型案例:

  • Apache Commons Collections(2015):影响大量 Java 应用,攻击者发送恶意序列化对象即可获取服务器权限。
  • Drupalgeddon 2(2018):Drupal 的 YAML 反序列化漏洞导致全球数十万个网站被攻击。

2. 对象属性篡改

即使无法直接执行代码,攻击者也可能篡改对象的关键属性:

// 用户对象被篡改权限
$user = unserialize($_COOKIE['user']);
// 原本 isAdmin=false,被篡改为 true

3. 拒绝服务(DoS)

通过构造特殊的序列化数据,触发无限递归或内存耗尽:

// 构造循环引用导致 StackOverflow
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("ref", map2);
map2.put("ref", map1);

四、漏洞检测方法

1. 黑盒检测

寻找序列化数据的特征:

语言序列化特征
PHPO:8:"ClassName"a:3:{...}
JavaAC ED(十六进制)开头的二进制数据,或 Base64 编码的 rO0...
Python80 开头的二进制数据(Pickle 协议)
JSON包含 @type$class 等类型指示字段

2. 白盒审计

检查代码中是否存在以下危险函数:

  • PHPunserialize()igbinary_unserialize()
  • JavaObjectInputStream.readObject()XStream.fromXML()
  • Pythonpickle.loads()yaml.load()(不安全模式)
  • Node.jseval() 处理 JSON、node-serialize

3. 工具辅助

  • ysoserial:Java 反序列化漏洞利用工具,生成各种 Payload
  • PHPGGC:PHP 反序列化 Payload 生成器
  • pickle-payloads:Python 反序列化测试工具

五、防御策略

1. 根本方案:避免反序列化不可信数据

最佳实践: 永远不要反序列化来自用户输入的数据。

# 错误:直接反序列化用户输入
data = pickle.loads(request.body)

# 正确:使用纯数据格式(JSON),手动构造对象
import json
json_data = json.loads(request.body)
user = User(name=json_data['name'], email=json_data['email'])

2. 输入验证与签名

如果必须接收序列化数据,应对其进行签名验证:

import hmac
import hashlib

def verify_serialized(data, signature, secret):
expected = hmac.new(secret, data, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, expected)

# 只处理通过验证的数据
if verify_serialized(data, sig, SECRET_KEY):
obj = pickle.loads(data)

3. 使用安全的替代方案

危险方案安全替代
pickle.loads()json.loads()
yaml.load()yaml.safe_load()
ObjectInputStreamJSON + 手动对象构造
unserialize()JSON + 类型白名单

4. 类型白名单(Java)

使用 ObjectInputFilter 限制可反序列化的类:

ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"!com.example.dangerous.*;java.base/*;!*"
);
ObjectInputStream ois = new ObjectInputStream(input);
ois.setObjectInputFilter(filter);

5. 运行时防护(RASP)

部署运行时应用自我保护,监控反序列化行为:

  • 检测异常的类加载
  • 拦截反序列化链中的危险调用
  • 记录可疑的反序列化操作

六、实战案例分析

案例:Java Apache Commons Collections 漏洞

漏洞原理:
TransformedMap 类在反序列化时会执行 transformer 链,攻击者构造包含 InvokerTransformer 的恶意对象,触发 Runtime.exec()

攻击链:

HashMap.readObject()
-> MapEntry.setValue()
-> ChainedTransformer.transform()
-> InvokerTransformer.transform()
-> Method.invoke()
-> Runtime.exec("calc.exe")

修复方案:

  1. 升级 Apache Commons Collections 到 3.2.2+ 或 4.4+
  2. 添加类白名单过滤
  3. 使用 SerialKiller 等防护库

七、总结

反序列化漏洞的破坏力极大,往往直接导致服务器沦陷。防御的核心原则是:不信任任何外部输入的序列化数据

要点措施
预防用 JSON 替代原生序列化
检测监控 readObjectunserialize 等函数
缓解类白名单 + 签名验证
应急升级组件 + 网络隔离

反序列化漏洞提醒我们:方便与安全往往不可兼得。在数据交换时,选择简单、透明的格式(如 JSON),远比复杂的二进制序列化更安全。