本文目录导读:

序列化漏洞(通常指反序列化漏洞)是Java、PHP、.NET等语言中非常危险的高危漏洞,攻击者可以通过构造恶意序列化数据,在服务端反序列化时触发任意代码执行、命令执行或拒绝服务。
以下从检测和修复两个维度,结合主流语言(Java、PHP、Python、.NET)给出具体方案。
如何检测序列化漏洞
代码审计(静态检测)
Java 重点搜索:
ObjectInputStream.readObject()ObjectInputStream.readUnshared()readObject()方法自定义实现的类- 使用了
RMI、JMX、JMS、Hessian、Kryo、XStream、Fastjson、Jackson等框架的默认反序列化
PHP 重点搜索:
unserialize()函数__wakeup()、__destruct()、__toString()魔术方法- 使用了
phpggc等工具可以生成 gadget chain 的库
Python 重点搜索:
pickle.load()/pickle.loads()- 使用了
PyYAML、ruamel.yaml且未限制Loader类型 jsonpickle等库
C# / .NET 重点搜索:
BinaryFormatter.Deserialize()SoapFormatter.Deserialize()NetDataContractSerializerJavaScriptSerializer.Deserialize()(旧版)TypeNameHandling设置为Auto或All的Newtonsoft.Json
动态测试(黑盒/灰盒)
网络流量抓包:
- 使用 Burp Suite、Wireshark 抓取 HTTP 请求体、Cookie、隐藏字段
- 查找以下特征:
- 二进制序列化开头:
aced0005(Java 序列化魔数) - PHP 序列化字符串:
a:1:{s:4:...或O:8: - Base64 编码的序列化内容(解码后检查魔数)
- 自定义编码后的序列化数据
- 二进制序列化开头:
工具辅助检测:
- Java:
ysoserial生成 payload,逐个尝试不同的 gadget chain - PHP:
phpggc生成 payload - Python:
pickle恶意构造 - 通用: 使用
SerializationDumper解码二进制序列化,判断是否可被利用
安全扫描器:
- Burp Suite 的
Java Deserialization Scanner插件(自动检测 JDK 版本、gadget chain) - Nessus / OpenVAS 中相关的反序列化插件
日志监测
- 检查日志中是否有大量
ClassNotFoundException、IOException异常 - 检查是否有
readObject调用导致的异常堆栈,特别是涉及Runtime、ProcessBuilder、FileInputStream的调用
如何修复序列化漏洞
最彻底:避免使用不安全的反序列化
- 不使用
ObjectInputStream、unserialize()、pickle.loads()直接处理不可信数据 - 改用安全的格式替代:
- JSON(使用
Jackson或Gson,关闭enableDefaultTyping) - Protocol Buffers(gRPC)
- MessagePack(但需注意实现是否安全)
- YAML(需使用
SafeLoader,如yaml.safe_load())
- JSON(使用
输入校验与验证
-
白名单验证: 反序列化前校验数据类型、类名是否在允许列表中
- Java 示例:
ObjectInputStream ois = new ObjectInputStream(input) { @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { // 只允许反序列化指定的安全类 if (!desc.getName().equals("com.example.SafeUser")) { throw new InvalidClassException("Unauthorized deserialization", desc.getName()); } return super.resolveClass(desc); } };
- Java 示例:
-
签名校验(HMAC): 对序列化数据进行完整性校验,防止被篡改
// 写入时 byte[] data = serialize(user); byte[] hmac = computeHmac(data, secretKey); send(data + hmac); // 读取时 byte[] receivedData = ...; byte[] receivedHmac = ...; if (!verifyHmac(receivedData, receivedHmac, secretKey)) { throw new SecurityException("数据被篡改"); } Object obj = deserialize(receivedData);
使用安全的反序列化工具或过滤机制
Java:
- 使用
SerialKiller:开源的 Java 反序列化防火墙,支持黑/白名单 - 使用
ValidatingObjectInputStream(Apache Commons IO) - 使用
Hessian或Kryo(需关闭默认的auto-type功能) - 使用
Fastjson的SafeMode(1.2.68+)ParserConfig.getGlobalInstance().setSafeMode(true);
PHP:
- 使用
hash_equals对序列化字符串做 HMAC 校验 - 尽量使用
json_decode+json_encode替代 - 升级 PHP 版本,某些旧版本自动反序列化问题更严重
Python:
- 永远不要对不可信数据使用
pickle - 使用
yaml.safe_load()代替yaml.load() - 使用
json+ 自定义object_hook控制反序列化行为
限制反序列化来源
- 不要将反序列化接口暴露在公网,仅限内网或经过认证的客户端使用
- 添加 IP 白名单、Token 校验、CSRF 防护
- 对反序列化的数据大小做上限限制(防止内存耗尽攻击)
// 限制数据大小 byte[] data = readFromSocket(); if (data.length > 1024 * 1024) { // 1MB throw new SecurityException("数据过大"); }
升级依赖库版本
很多反序列化漏洞源于第三方库的 gadget chain:
| 库 | 已知漏洞版本(示例) | 安全版本(示例) |
|---|---|---|
| Commons Collections | ≤ 3.2.1 | 2.2+ |
| Commons BeanUtils | ≤ 1.9.3 | 9.4+ |
| Fastjson | ≤ 1.2.24 | 2.83+ |
| Jackson | 默认开启enableDefaultTyping | 关闭或升级 |
| Spring Framework | ≤ 4.2.4 | 3+ |
使用 OWASP Dependency Check 或 Snyk 自动检测依赖中的已知漏洞。
使用 RASP(运行时应用自我保护)工具
- OpenRASP(百度开源):在运行时拦截
readObject、Runtime.exec等敏感调用 - Contrast Security、Aqua Security:商业级 RASP 产品
针对特定语言的最佳实践
Java 示例(综合方案):
// 1. 使用安全的ObjectInputStream
public class SafeObjectInputStream extends ObjectInputStream {
private static final Set<String> WHITELIST = Set.of(
"java.lang.String",
"java.util.ArrayList",
"com.example.SafeUser"
);
public SafeObjectInputStream(InputStream in) throws IOException {
super(in);
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (!WHITELIST.contains(desc.getName())) {
throw new InvalidClassException("Blocked class: " + desc.getName());
}
return super.resolveClass(desc);
}
}
// 2. HMAC 校验
public static byte[] serializeWithHmac(Serializable obj, byte[] key) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.close();
byte[] data = bos.toByteArray();
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key, "HmacSHA256"));
byte[] hmac = mac.doFinal(data);
ByteArrayOutputStream finalBos = new ByteArrayOutputStream();
finalBos.write(data);
finalBos.write(hmac);
return finalBos.toByteArray();
}
检测与修复优先级
| 优先级 | 动作 | 说明 |
|---|---|---|
| 最高 | 替换为安全格式(JSON/Protobuf) | 一劳永逸,彻底根除反序列化风险 |
| 高 | 升级依赖库版本 | 防止已知的gadget chain利用 |
| 高 | 添加HMAC签名校验 | 防止数据篡改,降低利用复杂度 |
| 中 | 实现白名单类验证 | 适用于必须使用序列化场景 |
| 中 | 使用RASP工具运行时检测 | 适合历史遗留系统无法快速改造 |
| 低 | 仅限制来源IP/认证 | 不能单独作为防护,需与其他配合 |
最终的铁律:永远不要对不可信数据直接进行反序列化。