Skip to content

JEP 290: Filter Incoming Serialization Data | 过滤传入的序列化数据

摘要

为了提高安全性和鲁棒性,允许对输入的对象序列化数据进行过滤。

目标

  • 提供一种灵活的机制,将应用程序可用的任何类的反序列化范围缩小到与上下文相关的类集合。

  • 为过滤器提供度量值,以验证反序列化期间图形大小和复杂性的正常行为。

  • 提供一种 RMI 导出对象的机制,以验证调用中预期的类。

  • 过滤机制不得要求对 ObjectInputStream 的现有子类进行子类化或修改。

  • 定义一个全局过滤器,可以通过属性或配置文件进行配置。

非目标

  • 定义或维护任何特定的允许或禁止的类的策略。

  • 修复特定类或实现的复杂性或完整性问题。

  • 在流中提供预先查看的能力。

  • 对对象内容提供细粒度的可见性。

成功指标

  • 在简单的类拒绝列表中,对性能影响最小且可衡量的。

动机

安全准则一贯要求在使用外部来源的输入之前进行验证。过滤机制将使对象序列化客户端能够更轻松地验证其输入,并使导出的 RMI 对象能够验证调用参数。

描述

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

对于流中的每个新对象,在实例化和反序列化对象之前,都会调用过滤器,并传入对象的类。对于基本类型或在流中以具体方式编码的 java.lang.String 实例,不会调用过滤器。对于每个数组,无论是原始类型数组、字符串数组还是对象数组,都会调用过滤器,并传入数组的类和数组的长度。对于对流中已读取的对象的每个引用,都会调用过滤器,以便检查深度、引用数量和流长度。如果启用了日志记录,则将过滤器操作记录到 java.io.serialization 记录器中。

对于 RMI,通过 UnicastServerRef 导出对象,可以在 MarshalInputStream 上设置过滤器,以在取消编组时验证调用参数。通过 UnicastRemoteObject 导出对象应支持设置用于取消编组的过滤器。

过程范围的过滤器

可以通过系统属性或配置文件配置进程范围的过滤器。如果提供了系统属性,则会覆盖安全属性的值。

  • 系统属性 jdk.serialFilter
  • 安全属性 conf/security/java.security 中的 jdk.serialFilter

过滤器配置为一系列模式,每个模式要么与流中的类名匹配,要么用于设置限制。模式之间用分号 ";" 分隔。空格是有意义的,并被视为模式的一部分。

限制模式包含一个 "=" 并设置一个限制。如果一个限制出现多次,则使用最后一个值。如果调用 ObjectInputFilter.checkInput(...) 中的任何值超过相应的限制,则过滤器返回 Status.REJECTED。无论模式的顺序如何,在处理类之前检查限制。

  • maxdepth=value - 图形的最大深度
  • maxrefs=value - 内部引用的最大数量
  • maxbytes=value - 输入流中的最大字节数
  • maxarray=value - 允许的最大数组大小

其他模式从左到右匹配从 Class::getName 返回的类或包名。如果类是数组类型,则要匹配的类或包是元素类型。任意维数的数组与元素类型相同处理。例如,模式 " !example.Foo" 拒绝创建任何 example.Foo 的实例或数组。

  • 如果模式以 " !" 开头,则如果模式的其余部分匹配,则拒绝该类,否则接受。
  • 如果模式包含 "/",非空前缀直到 "/" 是模块名。如果模块名与类的模块名匹配,则使用剩余的模式与类名进行匹配。如果没有 "/",则不比较模块名。
  • 如果模式以 " .**" 结尾,则匹配包中的任何类和所有子包中的类
  • 如果模式以 " .*" 结尾,则匹配包中的任何类
  • 如果模式以 " *" 结尾,则匹配以该模式为前缀的任何类。
  • 如果模式等于类名,则匹配。
  • 否则,状态未决。

ObjectInputFilter 接口和 API

对象输入过滤器接口由 RMI 和序列化的客户端实现,并提供了进程范围可配置过滤器的行为。

java
interface ObjectInputFilter {
    Status checkInput(FilterInput filterInfo);

    enum Status {
        UNDECIDED,
        ALLOWED,
        REJECTED;
    }

   interface FilterInfo {
         Class<?> serialClass();
         long arrayLength();
         long depth();
         long references();
         long streamBytes();
   }

    public static class Config {
        public static void setSerialFilter(ObjectInputFilter filter);
        public static ObjectInputFilter getSerialFilter(ObjectInputFilter filter) ;
        public static ObjectInputFilter createFilter(String patterns);
    }
}

ObjectInputStream 过滤器

ObjectInputStream 有额外的方法用于设置和获取当前过滤器。如果没有为 ObjectInputStream 设置过滤器,则使用全局过滤器(如果有)。

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

替代方案

修改现有的子类和方法,但这将需要更改,从而阻碍第三方实现的使用。

测试

无需更新现有测试。新的单元测试将测试带有序列化流、导出的 RMI 对象和全局过滤机制的过滤机制。

风险和假设

提供给支持拒绝列表、接受列表和流度量的过滤器的度量应该足够。在应用于已知用例时,可能会发现一些其他的过滤机制。

将引入 JDK 9 的新 API 和接口。将此功能回溯到之前的版本将需要引入特定于实现的 API,以避免对 Java SE 规范的旧版本进行更改。