Java序列化机制允许将对象转换为字节流进行存储或传输,反序列化则是将字节流恢复为对象的过程。如果反序列化时未对输入数据做校验,攻击者可以构造包含恶意类的序列化数据,触发任意代码执行,这就是反序列化漏洞的成因。序列化过滤机制就是用来限制反序列化过程中允许加载的类,从而防御这类攻击。

Java序列化过滤机制的核心原理
Java从8u121版本开始引入ObjectInputFilter接口,作为序列化过滤的核心组件。该接口会在反序列化过程中对每个要恢复的类进行检查,根据预设的规则决定是否允许该类被反序列化。过滤规则可以基于类的名称、类的父类、类的接口等多个维度进行配置。
过滤器的检查结果分为三种状态:
- ALLOWED:允许该类被反序列化
- REJECTED:拒绝该类被反序列化,会直接抛出
InvalidClassException - UNDECIDED:未做明确判断,交由下一个过滤器或者默认规则处理
内置序列化过滤器的使用方式
全局过滤器配置
可以通过JVM参数设置全局的序列化过滤器,对所有反序列化操作生效。配置格式为指定允许或者拒绝的类模式,多个模式之间用分号分隔。
例如只允许反序列化java.util包下的类,拒绝org.apache包下的类,可以添加JVM参数:
-Djdk.serialFilter=java.util.*;!org.apache.*
其中!前缀表示拒绝匹配的类,通配符*匹配任意字符。
局部过滤器设置
也可以在单个ObjectInputStream实例上设置过滤器,只对当前流的反序列化操作生效。示例代码如下:
import java.io.*;
import java.util.ArrayList;
public class SerialFilterDemo {
public static void main(String[] args) throws Exception {
// 构造测试序列化数据,这里简化为模拟反序列化过程
byte[] data = serializeData();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
// 设置局部过滤器,只允许ArrayList类被反序列化
ois.setObjectInputFilter(filterInfo -> {
String className = filterInfo.serialClass() == null ? null : filterInfo.serialClass().getName();
if (className != null && className.equals("java.util.ArrayList")) {
return ObjectInputFilter.Status.ALLOWED;
}
return ObjectInputFilter.Status.REJECTED;
});
// 执行反序列化
Object obj = ois.readObject();
System.out.println("反序列化结果:" + obj);
}
private static byte[] serializeData() throws Exception {
ArrayList<String> list = new ArrayList<>();
list.add("test");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(list);
return bos.toByteArray();
}
}
自定义过滤器的实现方案
如果需要更复杂的过滤逻辑,可以实现ObjectInputFilter接口自定义过滤器。比如需要同时校验类的包名和类的注解信息,可以编写如下自定义过滤器:
import java.io.ObjectInputFilter;
public class CustomSerialFilter implements ObjectInputFilter {
// 允许反序列化的包名白名单
private static final String[] ALLOW_PACKAGES = {"java.lang", "java.util", "com.example.dto"};
@Override
public Status checkInput(FilterInfo filterInfo) {
Class<?> serialClass = filterInfo.serialClass();
// 如果当前处理的是类信息
if (serialClass != null) {
String className = serialClass.getName();
// 检查是否在白名单包下
for (String pkg : ALLOW_PACKAGES) {
if (className.startsWith(pkg)) {
return Status.ALLOWED;
}
}
// 不在白名单的类直接拒绝
return Status.REJECTED;
}
// 非类信息的检查返回未决定,交由其他规则处理
return Status.UNDECIDED;
}
}
使用自定义过滤器时,既可以设置为全局过滤器,也可以设置为局部过滤器:
// 设置为局部过滤器 ObjectInputStream ois = new ObjectInputStream(inputStream); ois.setObjectInputFilter(new CustomSerialFilter());
防止反序列化漏洞的安全实践
最小权限原则配置过滤规则
尽量使用白名单模式配置过滤规则,只允许业务必需的类被反序列化,不要使用黑名单模式。黑名单很难覆盖所有可能的恶意类,比如攻击者可以构造新的利用链绕过黑名单限制,而白名单只要严格控制允许的范围,就能从根本上避免未知类的加载。
避免反序列化不可信数据
如果业务场景允许,尽量不要反序列化来自不可信来源的数据。如果必须反序列化外部输入,一定要先经过序列化过滤校验,同时不要将反序列化接口直接暴露给外部用户,避免攻击者直接构造恶意数据传入。
及时更新JDK版本
Java官方会持续修复序列化相关的安全漏洞,及时更新到最新的稳定JDK版本,可以获得官方提供的安全补丁和更完善的过滤机制支持。同时关注官方发布的安全公告,针对已知的序列化漏洞及时做对应的防护配置。
结合其他安全措施
序列化过滤只是防御反序列化漏洞的一层措施,还可以结合数据签名、加密传输等方式,确保序列化数据在传输过程中没有被篡改,进一步降低安全风险。如果反序列化的数据需要持久化,存储时也要做好权限控制,避免数据被恶意替换。
注意:序列化过滤机制不能完全替代其他安全措施,需要结合整体安全架构设计,才能最大程度降低反序列化漏洞带来的风险。
Java序列化反序列化漏洞序列化过滤ObjectInputFilter安全实践修改时间:2026-07-04 12:00:27