本文目录导读:

Java反序列化漏洞的防护需要从多个层面进行综合防御,以下是最有效的防护策略,按推荐程度排序:
使用安全的替代方案(最推荐)
- JSON:使用 Jackson、Gson 等,配合
@JsonTypeInfo的显式类型声明 - Protocol Buffers:Google 的高效序列化方案,自带类型安全
- Avro:Apache 的二进制序列化格式
// 使用 Jackson 替代 ObjectInputStream
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// 或更安全的配置
mapper.activateDefaultTyping(
BasicPolymorphicTypeValidator.builder().build(),
ObjectMapper.DefaultTyping.NON_FINAL
);
白名单验证(核心防护)
实现 ObjectInputFilter 接口(Java 9+),只允许特定类反序列化:
// 方式1:全局设置
ObjectInputFilter.Config.setSerialFilter(filter);
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"com.example.*;java.base/*;!*"
);
// 方式2:针对单个流
ObjectInputStream ois = new ObjectInputStream(inputStream);
ois.setObjectInputFilter(info -> {
Class<?> clazz = info.serialClass();
if (clazz != null) {
// 只允许业务类
if (clazz.getPackage().getName().startsWith("com.example")) {
return ObjectInputFilter.Status.ALLOWED;
}
return ObjectInputFilter.Status.REJECTED;
}
return ObjectInputFilter.Status.UNDECIDED;
});
白名单示例:
- 只允许:
com.example.*、java.util.*、java.lang.* - 禁止:
java.rmi.*、javax.management.*、com.sun.*
环境配置加固
JVM 参数防护(Java 9+):
-Djdk.serialFilter=com.example.*;java.base/*;!* -Djdk.serialFilter=maxdepth=10;maxarray=1000;maxbytes=10240
禁用 RMI/JMX(如果不需要):
-Dcom.sun.management.jmxremote=false -Djava.rmi.server.hostname=127.0.0.1
代码层面的防护
1 自定义 ObjectInputStream
public class SafeObjectInputStream extends ObjectInputStream {
private static final Set<String> ALLOWED_CLASSES = Set.of(
"java.lang.String",
"java.util.HashMap",
"com.example.MyBean"
);
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException {
String className = desc.getName();
if (!isAllowed(className)) {
throw new InvalidClassException("Unauthorized deserialization", className);
}
return super.resolveClass(desc);
}
private boolean isAllowed(String className) {
return ALLOWED_CLASSES.stream().anyMatch(className::startsWith);
}
}
2 使用 ValidatingObjectInputStream
// Apache Commons IO 提供
ValidatingObjectInputStream vois =
new ValidatingObjectInputStream(inputStream);
vois.accept(User.class, Order.class); // 只接受这两个类
vois.reject("java.rmi.**"); // 拒绝危险类
监控与检测
1 日志记录
ObjectInputStream ois = new ObjectInputStream(inputStream) {
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException {
String clsName = desc.getName();
logger.warn("Deserializing class: {}", clsName);
// 异常检测
if (isDangerousClass(clsName)) {
logger.error("Suspicious deserialization attempt: {}", clsName);
throw new SecurityException("Blocked dangerous class");
}
return super.resolveClass(desc);
}
};
2 使用安全扫描工具
- DeserLab:检测反序列化漏洞
- SerialKiller:Java 反序列化防护库
- OWASP Dependency Check:扫描第三方库漏洞
网络层防护
1 Web 应用防火墙(WAF)规则
# Nginx 规则示例
location /api {
if ($content_type ~ "application/x-java-serialized-object") {
return 403;
}
}
2 网络端口限制
- 禁用 1099(RMI Registry)
- 禁用 4444(JMX)
- 禁用 6666(RMI Activation)
完整的防护示例
public class SecureDeserializationService {
private static final ObjectInputFilter FILTER =
createStrictFilter();
@SuppressWarnings("unchecked")
public <T> T deserialize(byte[] data) {
try (ByteArrayInputStream bis = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bis)) {
// 设置过滤器
ois.setObjectInputFilter(FILTER);
// 限制大小
if (data.length > 10 * 1024 * 1024) { // 10MB限制
throw new SecurityException("Data too large");
}
return (T) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("Deserialization failed", e);
}
}
private static ObjectInputFilter createStrictFilter() {
return filterInfo -> {
Class<?> serialClass = filterInfo.serialClass();
if (serialClass == null) {
return ObjectInputFilter.Status.UNDECIDED;
}
String name = serialClass.getName();
// 白名单
if (name.startsWith("com.example.allowed.") ||
name.startsWith("java.util.") ||
name.equals("java.lang.String")) {
return ObjectInputFilter.Status.ALLOWED;
}
// 黑名单
if (name.startsWith("java.rmi.") ||
name.startsWith("javax.management.") ||
name.startsWith("com.sun.")) {
return ObjectInputFilter.Status.REJECTED;
}
// 默认拒绝
return ObjectInputFilter.Status.REJECTED;
};
}
}
核心原则
- 绝不反序列化不可信数据 - 如果必须,使用白名单
- 优先使用JSON/Protobuf - 避免Java原生序列化
- 最小权限原则 - 只反序列化必要的类
- 持续更新 - 及时修补已知漏洞(如Log4Shell)
- 深度防御 - 结合代码+环境+网络多层防护
通过以上策略,可以显著降低Java反序列化漏洞的风险,但需要根据具体业务场景选择最适合的方案。