JDK解决反序列化的方法
字数 1407 2025-08-27 12:33:42
Java反序列化漏洞与JDK解决方案详解
一、Java反序列化漏洞背景
Java反序列化漏洞是近年来安全领域的热点问题,几乎所有使用原生Java序列化的框架都可能受到反序列化攻击。Java序列化是Java内置功能,可以将对象转换为二进制数据(序列化),也能将二进制数据还原为对象(反序列化)。
主要应用场景
- JMS消息队列系统:通过序列化传输对象数据
- RESTful客户端:序列化存储OAuth token等对象
- Java远程方法调用(JMI):JVM间使用序列化通信
二、反序列化漏洞原理
反序列化过程
当应用代码触发反序列化时,ObjectInputStream将流数据初始化为对象。它会:
- 匹配字节流与JVM类路径中的类
- 根据流中的魔术字节识别对象类型
- 无法解析的类型会被视为
TC_OBJECT类型
漏洞成因
攻击者可构造特殊的字节流,利用JVM类路径中存在的特定类,通过已知利用链实现远程命令执行(RCE)。著名的工具如ysoserial可以生成此类攻击payload。
三、传统缓解方案
1. LookAheadObjectInputStream策略
通过继承ObjectInputStream并重写resolveClass()方法,验证类是否可加载。
实现方式
- 白名单:只允许特定类被反序列化
- 黑名单:阻止已知有问题的类
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
String name = desc.getName();
if(isBlacklisted(name)) {
throw new SecurityException("Deserialization is blocked for security reasons");
}
if(isWhitelisted(name)) {
throw new SecurityException("Deserialization is blocked for security reasons");
}
return super.resolveClass(desc);
}
优缺点
- 白名单:更安全,但需要维护允许的类列表
- 黑名单:易被绕过,需要持续更新
四、JDK新解决方案:序列化过滤(JEP 290)
Oracle在JDK 9中引入了serialization filtering机制,后被移植到旧版JDK。
核心接口:ObjectInputFilter
提供配置能力,在反序列化时验证输入数据。返回状态:
Status.ALLOWED:允许Status.REJECTED:拒绝Status.UNDECIDED:未决定
可访问的信息
arrayLength:数组长度depth:对象深度references:对象引用数streamBytes:流占用空间大小
五、过滤器配置方式
1. 自定义过滤器(Custom Filter)
适用于特定反序列化场景,实现ObjectInputFilter接口并重写checkInput方法。
static class VehicleFilter implements ObjectInputFilter {
final Class<?> clazz = Vehicle.class;
final long arrayLength = -1L;
final long totalObjectRefs = 1L;
final long depth = 1l;
final long streamBytes = 95L;
public Status checkInput(FilterInfo filterInfo) {
if (filterInfo.arrayLength() < this.arrayLength ||
filterInfo.arrayLength() > this.arrayLength ||
filterInfo.references() < this.totalObjectRefs ||
filterInfo.references() > this.totalObjectRefs ||
filterInfo.depth() < this.depth ||
filterInfo.depth() > this.depth ||
filterInfo.streamBytes() < this.streamBytes ||
filterInfo.streamBytes() > this.streamBytes) {
return Status.REJECTED;
}
if (filterInfo.serialClass() == null) {
return Status.UNDECIDED;
}
if (filterInfo.serialClass() != null && filterInfo.serialClass() == this.clazz) {
return Status.ALLOWED;
} else {
return Status.REJECTED;
}
}
}
2. 全局过滤器(Process-wide Filter)
通过jdk.serialFilter配置,可作为系统属性或安全属性。
过滤规则示例
-
限制参数:
maxdepth=value // 最大图深度 maxrefs=value // 最大内部引用数 maxbytes=value // 输入流最大字节数 maxarray=value // 最大数组大小 -
类名匹配:
"jdk.serialFilter=org.example.Vehicle;!*" // 只允许特定类 "jdk.serialFilter=org.example.**" // 允许包及子包所有类 "jdk.serialFilter=org.example.*" // 只允许包内类 "jdk.serialFilter=*;!org.example.**" // 拒绝特定包
3. 内置过滤器(Built-in Filters)
主要用于RMI和分布式垃圾回收(DGC)。
RMI Registry白名单
java.lang.Number
java.rmi.Remote
java.lang.reflect.Proxy
sun.rmi.server.UnicastRef
sun.rmi.server.RMIClientSocketFactory
sun.rmi.server.RMIServerSocketFactory
java.rmi.activation.ActivationID
java.rmi.server.UID
DGC白名单
java.rmi.server.ObjID
java.rmi.server.UID
java.rmi.dgc.VMID
java.rmi.dgc.Lease
可通过sun.rmi.registry.registryFilter和sun.rmi.transport.dgcFilter添加自定义过滤器。
六、总结
Java反序列化本身不是漏洞,问题在于反序列化不受信任的数据。JEP 290引入的过滤机制为开发者提供了标准化的解决方案:
- 支持自定义过滤器满足特定场景需求
- 提供全局过滤器配置简化部署
- 内置RMI和DGC的安全过滤器
- 细粒度的过滤控制(类名、大小、深度等)
开发者应根据实际需求选择合适的过滤策略,优先考虑白名单方式,并结合JDK提供的过滤机制,有效缓解反序列化攻击风险。