在 java 对象反序列化时,使用 objectinputfilter 可以拦截并检查即将被读取的对象类型,从而防止不安全的类加载。具体做法是在创建 objectinputstream 后,通过 setobjectinputfilter() 方法设置一个过滤器实例,该实例的 checkinput() 方法会针对每个待反序列化的类进行判断,返回 allow、block 或 reject 来决定是否允许该对象被反序列化。

如何配置白名单和黑名单策略
可以通过实现 ObjectInputFilter 接口并重写 checkInput() 方法来定义具体的过滤逻辑。通常的做法是维护一个允许的类名列表(白名单)或禁止的类名列表(黑名单),在方法中调用 info.serialClass() 获取当前类的名称,并与预设规则进行比对。如果类名匹配白名单则返回 ObjectInputFilter.Status.ALLOW,否则返回 ObjectInputFilter.Status.REJECT。这种机制能有效阻止攻击者构造恶意 payload 进行反序列化攻击。

import java.io.*;
import java.util.Arrays;
import java.util.List;
public class SecureSerialization {
public static void main(String[] args) throws Exception {
List<String> allowedClasses = Arrays.asList(
"java.lang.String",
"java.util.ArrayList"
);
// 创建自定义过滤器
ObjectInputFilter filter = info -> {
String className = info.serialClass() != null ? info.serialClass().getName() : null;
if (className == null) return ObjectInputFilter.Status.UNDECIDED;
return allowedClasses.contains(className)
? ObjectInputFilter.Status.ALLOW
: ObjectInputFilter.Status.REJECT;
};
// 模拟写入
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject("Hello");
oos.writeObject(Arrays.asList("Item1"));
}
// 模拟读取
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
try (ObjectInputStream ois = new ObjectInputStream(bais)) {
ois.setObjectInputFilter(filter);
System.out.println(ois.readObject());
System.out.println(ois.readObject());
}
}
}
如何处理嵌套对象和数组类型的过滤
当反序列化包含嵌套对象或数组时,ObjectInputFilter 会对每一个独立的对象引用进行检查。对于数组,它会检查数组的元素类型;对于嵌套对象,它会递归地检查内部对象的类。这意味着你不需要手动遍历对象图,过滤器会自动处理所有层级。如果任何一个内部对象被拒绝,整个反序列化过程将抛出 InvalidClassException。此外,还可以利用 info.depth() 和 info.references() 来监控反序列化的深度和引用计数,防止资源耗尽攻击。
立即学习“Java免费学习笔记(深入)”;

import java.io.*;
import java.util.*;
public class NestedFilterDemo {
public static void main(String[] args) throws Exception {
ObjectInputFilter filter = info -> {
Class<?> clazz = info.serialClass();
if (clazz == null) return ObjectInputFilter.Status.UNDECIDED;
// 允许基本类型包装类和集合
if (clazz.isPrimitive() || Number.class.isAssignableFrom(clazz)
|| Collection.class.isAssignableFrom(clazz)) {
return ObjectInputFilter.Status.ALLOW;
}
// 拒绝其他非标准类
return ObjectInputFilter.Status.REJECT;
};
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
Map<String, Object> data = new HashMap<>();
data.put("name", "Test");
data.put("count", 42);
oos.writeObject(data);
}
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
try (ObjectInputStream ois = new ObjectInputStream(bais)) {
ois.setObjectInputFilter(filter);
Object obj = ois.readObject();
System.out.println("Deserialized: " + obj);
}
}
}
过滤器在遇到未知类时会返回什么状态
当 ObjectInputFilter 遇到未在预设规则中明确指定的类,或者 serialClass() 返回 null 时,如果代码中没有显式返回 ALLOW 或 REJECT,默认行为取决于过滤器的实现。在上述示例中,我们显式返回了 UNDECIDED,这表示让系统使用默认的过滤策略。如果希望严格限制,应在 if 条件之外添加一个默认的 return ObjectInputFilter.Status.REJECT。这样,任何未被白名单明确允许的类都将被拒绝,确保只有受信任的类型才能被反序列化。


















