跳到主要内容

JEP 290:过滤传入的序列化数据

概括

允许过滤传入的对象序列化数据流,以提高安全性和稳健性。

目标

  • 提供灵活的机制来将可反序列化的类范围从应用程序可用的任何类缩小到适合上下文的类集。

  • 在反序列化期间向过滤器提供图表大小和复杂性的指标,以验证正常的图表行为。

  • 为 RMI 导出的对象提供一种机制来验证调用中所需的类。

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

  • 定义可以通过属性或配置文件配置的全局过滤器。

非目标

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

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

  • 在流中提供前瞻功能。

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

成功指标

  • 对类的简单拒绝列表的可测量性能影响最小

动机

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

描述

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

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

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

全过程过滤器

进程范围的过滤器是通过系统属性或配置文件配置的。系统属性(如果提供)将取代安全属性值。

  • 系统属性jdk.serialFilter
  • 安全财产jdk.serialFilterconf/security/java.security

过滤器被配置为一系列模式,每个模式要么与流中的类名称相匹配,要么与限制相匹配。模式由“;”分隔(分号)。空白很重要,被认为是模式的一部分。

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

  • maxdepth=value— 图的最大深度
  • maxrefs=value— 内部参考的最大数量
  • maxbytes=value— 输入流中的最大字节数
  • maxarray=value— 允许的最大数组大小

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

  • 如果模式以“ !”开头,如果模式的其余部分匹配,则该类将被拒绝,否则将被接受
  • 如果模式包含“/”,则“/”之前的非空前缀是模块名称。如果模块名称与类的模块名称匹配,则其余模式与类名称匹配。如果没有“/”,则不比较模块名称。
  • 如果模式以“ .**”结尾,则它匹配包和所有子包中的任何类
  • 如果模式以“ .*”结尾,则它与包中的任何类匹配
  • 如果模式以“ *”结尾,则它匹配以该模式作为前缀的任何类。
  • 如果模式等于类名,则匹配。
  • 否则,状态未定。

ObjectInputFilter 接口和 API

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

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则使用全局过滤器(如果有)。

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

备择方案

修改现有的子类和方法,但这需要进行一些更改,从而禁止在第三方实现中使用。

测试

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

风险和假设

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

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