java 对象序列化中的白名单过滤主要通过配置 objectinputfilter 来实现,该机制允许开发者在反序列化过程中精确控制哪些类可以被实例化。具体做法是在调用 objectinputstream.readobject() 之前,通过 objectinputstream.setobjectinputfilter() 方法设置一个自定义的过滤器实例,并在其 checkinput 方法中根据传入的 class 对象判断是否允许读取。如果类名不在白名单内或不符合安全规范,直接返回 status.rejected 以阻断反序列化,从而有效防止恶意类加载漏洞。

如何基于包名前缀实现动态白名单校验
基于包名前缀实现动态白名单校验的核心在于利用 ObjectInputFilter.Config.createFilter() 提供的便捷 API,或者手动实现 ObjectInputFilter 接口时提取类的包路径进行匹配。这种做法比硬编码类名更灵活,能够覆盖整个业务模块下的所有实体类。当反序列化请求到达时,过滤器会检查类的命名空间,若属于信任的包(如 com.example.model)则放行,否则拒绝。这种方式特别适用于类数量较多且结构清晰的场景,避免了维护冗长的类名列表。

import java.io.*;
import java.util.*;
public class WhitelistDemo {
public static void main(String[] args) throws Exception {
// 定义白名单包前缀
String whitelistPrefix = "com.example.model.";
// 创建并设置过滤器
ObjectInputFilter filter = (filterInfo) -> {
Class> clazz = filterInfo.serialClass();
if (clazz == null) return ObjectInputFilter.Status.UNDECIDED;
String className = clazz.getName();
if (className.startsWith(whitelistPrefix)) {
return ObjectInputFilter.Status.ALLOWED;
}
return ObjectInputFilter.Status.REJECTED;
};
// 模拟写入
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(new User("Alice", 25));
}
// 模拟读取
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
try (ObjectInputStream ois = new ObjectInputStream(bais)) {
ois.setObjectInputFilter(filter);
User user = (User) ois.readObject();
System.out.println("Read: " + user);
}
}
}
class User implements Serializable {
private String name;
private int age;
public User(String name, int age) { this.name = name; this.age = age; }
public String toString() { return "User{name='" + name + "', age=" + age + "}"; }
}
如何针对特定类名进行精确白名单过滤
针对特定类名进行精确白名单过滤适用于对安全性要求极高、只允许极少数已知类参与序列化的场景。实现方式是将允许的类名存入一个 Set 集合中,在过滤器的 checkInput 方法中进行查找匹配。只有当当前反序列化的类完全匹配白名单中的某个条目时,才返回 ALLOWED。任何未列出的类,包括子类或同名不同包的类,都会被视为非法并被拒绝。这种细粒度的控制能最大程度地缩小攻击面。

import java.io.*;
import java.util.*;
public class ExactWhitelistDemo {
public static void main(String[] args) throws Exception {
Set<string> allowedClasses = new HashSet(Arrays.asList(
"com.example.model.User",
"com.example.model.Address"
));
ObjectInputFilter filter = (filterInfo) -> {
Class> clazz = filterInfo.serialClass();
if (clazz == null) return ObjectInputFilter.Status.UNDECIDED;
if (allowedClasses.contains(clazz.getName())) {
return ObjectInputFilter.Status.ALLOWED;
}
return ObjectInputFilter.Status.REJECTED;
};
// 测试合法类
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(new User("Bob", 30));
}
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
try (ObjectInputStream ois = new ObjectInputStream(bais)) {
ois.setObjectInputFilter(filter);
User user = (User) ois.readObject();
System.out.println("Success: " + user);
} catch (Exception e) {
System.err.println("Failed: " + e.getMessage());
}
}
}
class User implements Serializable {
private String name;
private int age;
public User(String name, int age) { this.name = name; this.age = age; }
public String toString() { return "User{name='" + name + "', age=" + age + "}"; }
}</string>
当白名单不包含某类时会发生什么
当白名单不包含某类时,ObjectInputFilter 会立即终止该对象的反序列化过程,并抛出 InvalidObjectException 或 SecurityException。具体行为取决于过滤器的实现:如果返回 REJECTED,JVM 会认为该对象不安全,阻止其被构建。对于数组或集合,如果其中的元素类型不在白名单内,整个容器也会失败。这种即时拒绝机制确保了即使攻击者构造了恶意的序列化数据流,也无法在服务器端实例化危险对象,从而保障了系统的安全。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











