Java8之后的版本中反序列化的防护

更新记录:Java9(JEP 290),Java17JEP 415),Java(now)–2024.08.24

Java9

JEP 290: Filter Incoming Serialization Data

“核心机制是由序列化客户端实现并设置在ObjectInputStream.在反序列化过程中调用过滤器接口方法,以验证正在反序列化的类、正在创建的数组的大小以及描述流长度、流深度和解码流时的引用数量的指标。过滤器返回接受、拒绝或保留未决定状态的状态。”

以上是jeps中的描述

简单说,java9在ObjectInputStream中添加了调用过滤器检查类的方法,这个过滤器包括了全局过滤器和局部过滤器,有限调用全局过滤器。

public class ObjectInputStream ... {
    public final void setObjectInputFilter(ObjectInputFilter filter);
    public final ObjectInputFilter getObjectInputFilter(ObjectInputFilter filter);
}

在java9中查看具体的内容

  • 获取安全管理的配置
  • 判断是否具有SERIAL_FILTER_PERMISSION的权限
  • 判断过滤器是否为空
  • 设置过滤器

具体查看getSerialFilter

查看filterCheck

注意到checkinput

注意到对于非基本类型

具体使用的时候可以通过定义filter实现

  public MyFilter() {
        this.filters = Arrays.asList(
            clazz -> clazz == String.class ? Status.ACCEPTED : Status.UNDECIDED,
            clazz -> clazz == Integer.class ? Status.ACCEPTED : Status.UNDECIDED,
            clazz -> clazz.getName().startsWith("com.abc") ? Status.ACCEPTED : Status.REJECTED
        );
    }

Java 17

JEP 415: Context-Specific Deserialization Filters:

简单说就是在java9的基础上,将过滤器变成可以动态调整的。

Java9中通过synchronized(serialFilterLock) 确保在同一时刻只有一个线程能够执行这个代码块。防止多个线程同时访问和修改serialFilter,从而导致线程安全问题。

查看Java17中setObjectInputFilter

if (totalObjectRefs > 0 && !Caches.SET_FILTER_AFTER_READ) {
    throw new IllegalStateException(
            "filter can not be set after an object has been read");
}

在这里确保了在对象已经开始反序列化后,不能再设置或更改过滤器。

这里删除了java9中的synchronized (serialFilterLock):

===当前java===

在当前java中查看具体的内容

具体操作:

  • 获取安全管理的配置
  • 判断是否具有SERIAL_FILTER_PERMISSION的权限
  • 判断是否已经读取对象
  • 判断是否已经设置过滤器
  • 判断之前的过滤器和新的过滤是否为空
  • 设置新的过滤器

具体查看getSerialFilterFactory

结合apply(),返回一个包含了之前过滤器和新过滤器的ObjectInputFilter对象

查看ObjectInputStream#filterCheck方法

注意到checkInput方法

查看ObjectInputFilter#checkinput方法

根据对象的引用数量、深度、字节数和数组长度来做初步判断,并对数组和基本类型采取特定处理。

对于非基本类型采用以下代码

调用调用一系列的过滤器,获取第一个不是Status.UNDECIDED状态的值,如果没有则返回一个空的Optional

具体使用的时候可以通过定义filters进行使用

  public MyFilter() {
        this.filters = Arrays.asList(
            clazz -> clazz == String.class ? Status.ACCEPTED : Status.UNDECIDED,
            clazz -> clazz == Integer.class ? Status.ACCEPTED : Status.UNDECIDED,
            clazz -> clazz.getName().startsWith("com.abc") ? Status.ACCEPTED : Status.REJECTED
        );
    }

也可以设置全局过滤器进行反序列化的校验。

不足

缺少中间版本的内容:

  • Java 10 & 11 :改进JEP 290 ;
  • Java 12 :再次改进JEP 290;
  • Java 16 :强制强封装。

      参考文章

      JDK9新特性-JEP 290:过滤传入的序列化数据 | JEPs
      分析JEP 290机制的Java实现 – FreeBuf网络安全行业门户

      JDK17新特性-JEP 415:上下文特定的反序列化过滤器 | JEPs
      Java 17 更新(12):支持上下文的序列化过滤器,又一次给序列化打补丁-腾讯云开发者社区-腾讯云 (tencent.com)

      上一篇
      下一篇