- 白名单策略实施:
- 定义白名单类集合:
- 梳理应用中实际需要反序列化的类,创建一个明确的白名单。例如,如果应用主要涉及用户信息、订单信息的反序列化,将对应的
User
类、Order
类等加入白名单。
- 可以在配置文件(如
properties
或xml
文件)中定义白名单,格式如下:
whitelist.classes=com.example.User,com.example.Order
- 在反序列化过程中检查:
- 自定义一个
ObjectInputStream
的子类,重写resolveClass
方法。
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.util.Arrays;
import java.util.List;
public class SafeObjectInputStream extends ObjectInputStream {
private final List<String> whitelist;
public SafeObjectInputStream(InputStream in, List<String> whitelist) throws IOException {
super(in);
this.whitelist = whitelist;
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
String className = desc.getName();
if (!whitelist.contains(className)) {
throw new InvalidClassException("Unauthorized class: " + className);
}
return super.resolveClass(desc);
}
}
- 在反序列化的代码处,使用自定义的`SafeObjectInputStream`。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Arrays;
import java.util.List;
public class DeserializeExample {
public static void main(String[] args) {
List<String> whitelist = Arrays.asList("com.example.User", "com.example.Order");
try (ObjectInputStream ois = new SafeObjectInputStream(new FileInputStream("serializedObject"), whitelist)) {
Object obj = ois.readObject();
// 处理反序列化后的对象
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
- 类加载器限制:
- 自定义类加载器:
- 创建一个自定义类加载器,继承自
ClassLoader
。
public class RestrictedClassLoader extends ClassLoader {
private final List<String> allowedPackages;
public RestrictedClassLoader(List<String> allowedPackages, ClassLoader parent) {
super(parent);
this.allowedPackages = allowedPackages;
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (!isAllowedPackage(name)) {
throw new ClassNotFoundException("Class not allowed: " + name);
}
return super.loadClass(name, resolve);
}
private boolean isAllowedPackage(String className) {
for (String allowedPackage : allowedPackages) {
if (className.startsWith(allowedPackage)) {
return true;
}
}
return false;
}
}
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Arrays;
import java.util.List;
public class DeserializeExample {
public static void main(String[] args) {
List<String> allowedPackages = Arrays.asList("com.example.");
ClassLoader restrictedClassLoader = new RestrictedClassLoader(allowedPackages, DeserializeExample.class.getClassLoader());
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("serializedObject")) {
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
return restrictedClassLoader.loadClass(desc.getName());
}
}) {
Object obj = ois.readObject();
// 处理反序列化后的对象
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
- 最小化对现有业务逻辑影响:
- 封装修改:
- 将白名单策略和类加载器限制的代码封装在独立的模块或工具类中。这样,对于现有的业务逻辑代码,只需要在反序列化调用处进行少量修改,引入新的类(如
SafeObjectInputStream
)和配置(白名单配置)即可。
- 逐步过渡:
- 可以先在测试环境全面测试修复方案,确保不影响现有业务功能。然后在生产环境采用灰度发布的方式,逐步将修复后的代码上线,观察业务运行情况,及时处理可能出现的兼容性问题。