- 功能接口:
- 这是一个功能接口,因此可以用作lambda表达式或方法引用的赋值目标。
警告:反序列化不受信任的数据本质上是危险的,应该避免使用。不受信任的数据应根据Java SE的安全编码准则中的“序列化和反序列化”部分进行仔细验证。 序列化过滤描述了防御性使用序列过滤器的最佳实践。
为了防止反序列化漏洞,应用程序开发人员需要清楚地描述每个组件或库可以反序列化的对象。对于每个上下文和用例,开发人员应构建并应用适当的过滤器。
反序列化过滤工厂和过滤器
反序列化过滤的部分包括过滤器、复合过滤器和过滤器工厂。每个过滤器对类和资源限制执行检查,以确定其状态为拒绝、允许或未决定。过滤器可以由其他过滤器组成,并合并或组合它们的结果。过滤器工厂负责为每个ObjectInputStream
建立和更新过滤器。
对于简单情况,可以为整个应用程序设置一个静态的JVM范围过滤器,而无需设置过滤器工厂。可以通过在命令行上设置系统属性或调用Config.setSerialFilter来设置JVM范围过滤器。不需要指定自定义过滤器工厂,默认为内置过滤器工厂。内置过滤器工厂为每个ObjectInputStream提供了静态的JVM范围过滤器。
例如,可以设置一个允许示例类、允许java.base
模块中的类并拒绝所有其他类的过滤器:作为命令行属性:
% java -Djdk.serialFilter="example.*;java.base/*;!*" ...
var filter = ObjectInputFilter.Config.createFilter("example.*;java.base/*;!*")
ObjectInputFilter.Config.setSerialFilter(filter);
在具有多个执行上下文的应用程序中,应用程序可以提供一个过滤器工厂,以保护每个上下文,为每个上下文提供自定义过滤器。当构造流时,过滤器工厂被调用以从可用信息(包括当前线程本地状态、调用者层次结构、库、模块和类加载器)中识别执行上下文。在那时,过滤器工厂策略用于创建或选择基于上下文的特定过滤器或过滤器组合。JVM范围的反序列化过滤器工厂确保可以在每个ObjectInputStream
上设置特定于上下文的反序列化过滤器,并可以检查从流中读取的每个对象。
调用过滤器工厂
JVM范围的过滤器工厂是在每个ObjectInputStream
被构造时以及设置流特定过滤器时调用的函数。参数是当前过滤器和请求的过滤器,它返回用于流的过滤器。当从ObjectInputStream构造函数调用时,第一个参数是null
,第二个参数是静态的JVM范围过滤器。当从ObjectInputStream.setObjectInputFilter
调用时,第一个参数是当前设置在流上的过滤器(在构造函数中设置),第二个参数是给定给ObjectInputStream.setObjectInputFilter
的过滤器。当前过滤器和新过滤器都可以是null
,工厂可以返回null
。请注意,过滤器工厂实现也可以使用其可用的任何上下文信息,例如从应用程序线程上下文中提取的信息或其调用堆栈,以组合和合并新过滤器。它不仅限于仅使用其两个参数。
活动的反序列化过滤器工厂要么是:
- 通过
ObjectInputFilter.Config.setSerialFilterFactory(BinaryOperator)
或系统属性jdk.serialFilterFactory
或安全属性jdk.serialFilterFactory
设置的应用程序特定过滤器工厂。 - 否则,当从ObjectInputStream构造函数调用时,内置的反序列化过滤器工厂提供静态的JVM范围过滤器,并在从
ObjectInputStream.setObjectInputFilter(ObjectInputFilter)
调用时替换静态过滤器。请参阅getSerialFilterFactory。
过滤器
过滤器可以根据模式字符串或基于类的谓词来允许或拒绝类。过滤器的checkInput(FilterInfo)
方法在读取对象时零次或多次调用。该方法被调用以验证类、每个数组的长度、从流中读取的对象数量、图的深度以及从流中读取的总字节数。
复合过滤器组合或检查其他过滤器的结果。merge(filter, anotherFilter)
过滤器组合两个过滤器的状态值。rejectUndecidedClass(filter)
检查过滤器对类的结果,当状态为UNDECIDED
时。在许多情况下,任何未被过滤器ALLOWED
的类应该被REJECTED
。
反序列化过滤器确定参数是允许还是拒绝,并应返回适当的状态:ALLOWED
或REJECTED
。如果过滤器无法确定状态,则应返回UNDECIDED
。过滤器应设计用于特定用例和预期类型。为特定用例设计的过滤器可能会传递超出过滤器范围的类。如果过滤器的目的是拒绝类,则可以拒绝与候选类匹配的类,并对其他类报告UNDECIDED
。过滤器可能会被调用,类等于null
,arrayLength
等于-1,深度、引用数和流大小,并返回仅反映其中一个或一些值的状态。这使得过滤器可以具体说明其报告的选择,并在不强制要求允许或拒绝状态的情况下使用其他过滤器。
过滤器模型示例
对于简单应用程序,列出允许或拒绝的类的单个预定义过滤器可能足以管理反序列化意外类的风险。对于由多个模块或库组成的应用程序,应用程序的结构可以用于识别每个应用程序上下文中每个ObjectInputStream
应允许或拒绝的类。当构造每个流时,反序列化过滤器工厂被调用,并可以检查线程或程序以确定要应用的特定于上下文的过滤器。一些可能的示例:
- 线程本地状态可以保存要应用的过滤器或与流特定过滤器组合。过滤器可以从应用程序或库维护的虚拟过滤器堆栈中推送和弹出。
- 过滤器工厂可以识别反序列化方法的调用者,并使用模块或库上下文选择过滤器或组合适当的特定于上下文的过滤器。一种机制可以识别具有受限或无限制访问序列化类的被调用者,并相应地选择过滤器。
在线程中过滤每个反序列化的示例
此类显示了应用程序提供的过滤器工厂如何组合过滤器以检查在线程中发生的每个反序列化操作。它定义了一个线程本地变量来保存特定于线程的过滤器,并构造一个过滤器工厂,将该过滤器与静态的JVM范围过滤器和流特定过滤器组合,拒绝任何未被这两个过滤器处理的类。如果设置了流特定过滤器并且不接受或拒绝类,则应用组合的JVM范围过滤器和线程过滤器。doWithSerialFilter
方法设置线程特定过滤器并调用应用程序提供的Runnable
。
public static final class FilterInThread implements BinaryOperator<ObjectInputFilter> {
private final ThreadLocal<ObjectInputFilter> filterThreadLocal = new ThreadLocal<>();
// 构造一个FilterInThread反序列化过滤器工厂。
public FilterInThread() {}
// 返回静态JVM全局过滤器、特定线程过滤器和流特定过滤器的组合过滤器。
public ObjectInputFilter apply(ObjectInputFilter curr, ObjectInputFilter next) {
if (curr == null) {
// 从OIS构造函数调用或可能是没有当前过滤器的OIS.setObjectInputFilter调用
var filter = filterThreadLocal.get();
if (filter != null) {
// 合并以调用线程本地过滤器,然后调用JVM全局过滤器(如果有)
filter = ObjectInputFilter.merge(filter, next);
return ObjectInputFilter.rejectUndecidedClass(filter);
}
return (next == null) ? null : ObjectInputFilter.rejectUndecidedClass(next);
} else {
// 从OIS.setObjectInputFilter调用,有当前过滤器和流特定过滤器。
// curr过滤器已经包含线程过滤器和静态JVM全局过滤器以及拒绝未决类
// 如果有流特定过滤器,合并以调用它,然后调用当前过滤器。
if (next != null) {
return ObjectInputFilter.merge(next, curr);
}
return curr;
}
}
// 将过滤器应用于线程并调用可运行对象。
public void doWithSerialFilter(ObjectInputFilter filter, Runnable runnable) {
var prevFilter = filterThreadLocal.get();
try {
filterThreadLocal.set(filter);
runnable.run();
} finally {
filterThreadLocal.set(prevFilter);
}
}
}
使用过滤器工厂
要使用FilterInThread
实用程序,请创建一个实例并将其配置为JVM全局过滤器工厂。 doWithSerialFilter
方法使用一个过滤器被调用,允许示例应用程序和核心类:
// 创建一个FilterInThread过滤器工厂并设置
var filterInThread = new FilterInThread();
ObjectInputFilter.Config.setSerialFilterFactory(filterInThread);
// 创建一个过滤器以允许example.*类并拒绝所有其他类
var filter = ObjectInputFilter.Config.createFilter("example.*;java.base/*;!*");
filterInThread.doWithSerialFilter(filter, () -> {
byte[] bytes = ...;
var o = deserializeObject(bytes);
});
除非另有说明,在此接口及其嵌套类中向方法传递null
参数将导致抛出NullPointerException
。
- 自JDK版本:
- 9
- 参见:
-
Nested Class Summary
Modifier and TypeInterfaceDescriptionstatic final class
一个实用类,用于设置和获取JVM范围的反序列化过滤器工厂,静态JVM范围的过滤器,或者根据模式字符串创建过滤器。static interface
FilterInfo提供了有关当前正在反序列化的对象以及ObjectInputStream
状态的信息。static enum
对类、数组长度、引用数量、深度和流大小的检查状态。 -
Method Summary
Modifier and TypeMethodDescriptionstatic ObjectInputFilter
allowFilter
(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus) 返回一个过滤器,如果类的谓词为true
,则返回Status.ALLOWED
。checkInput
(ObjectInputFilter.FilterInfo filterInfo) 检查类、数组长度、对象引用数量、深度、流大小和其他可用的过滤信息。static ObjectInputFilter
merge
(ObjectInputFilter filter, ObjectInputFilter anotherFilter) 返回一个合并过滤器状态和另一个过滤器状态的过滤器。static ObjectInputFilter
rejectFilter
(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus) 返回一个过滤器,如果类的谓词为true
,则返回Status.REJECTED
。static ObjectInputFilter
返回一个调用给定过滤器并将UNDECIDED
映射为REJECTED
的过滤器,对于类,有一些特殊情况,否则返回状态。
-
Method Details
-
checkInput
检查类、数组长度、对象引用数量、深度、流大小和其他可用的过滤信息。此方法的实现检查在反序列化过程中创建的对象图的内容。过滤器返回Status.ALLOWED
、Status.REJECTED
或Status.UNDECIDED
。如果
filterInfo.serialClass()
为non-null
,则有一个要检查的类。如果serialClass()
为null
,则没有类,信息仅包含与正在反序列化的图的深度、引用数量和读取流大小相关的度量。- API注释:
-
每个实现
checkInput
的过滤器应返回ObjectInputFilter.Status
值之一。返回null
可能导致NullPointerException
或其他不可预测的行为。 - 参数:
-
filterInfo
- 提供有关当前正在反序列化的对象(如果有)以及ObjectInputStream
状态的信息 - 返回:
-
如果接受,则返回
Status.ALLOWED
,如果拒绝,则返回Status.REJECTED
,如果未决,则返回Status.UNDECIDED
。
-
allowFilter
static ObjectInputFilter allowFilter(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus) 返回一个过滤器,如果类的谓词为true
,则返回Status.ALLOWED
。根据non-null
类的谓词返回ALLOWED
或otherStatus
,如果类为null
,则返回UNDECIDED
。当调用过滤器的
checkInput(info)
方法时,将谓词应用于info.serialClass()
,返回状态为:例如,创建一个允许从平台或引导类加载器加载的任何类的过滤器。
ObjectInputFilter f = allowFilter(cl -> cl.getClassLoader() == ClassLoader.getPlatformClassLoader() || cl.getClassLoader() == null, Status.UNDECIDED);
- 参数:
-
predicate
- 用于测试非空类的谓词 -
otherStatus
- 如果谓词为false
时使用的状态 - 返回:
-
返回一个过滤器,如果类的谓词为
true
,则返回ALLOWED
- 自JDK版本:
- 17
-
rejectFilter
static ObjectInputFilter rejectFilter(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus) 返回一个过滤器,如果类的谓词为true
,则返回Status.REJECTED
。根据non-null
类的谓词返回REJECTED
或otherStatus
,如果类为null
,则返回UNDECIDED
。当调用过滤器的checkInput(info)
方法时,将谓词应用于serialClass()
,返回状态为:例如,创建一个拒绝从应用程序类加载器加载的任何类的过滤器。
ObjectInputFilter f = rejectFilter(cl -> cl.getClassLoader() == ClassLoader.ClassLoader.getSystemClassLoader(), Status.UNDECIDED);
- 参数:
-
predicate
- 用于测试非空类的谓词 -
otherStatus
- 如果谓词为false
时使用的状态 - 返回:
-
返回一个过滤器,如果类的谓词为
true
,则返回REJECTED
- 自JDK版本:
- 17
-
merge
返回一个合并过滤器状态和另一个过滤器状态的过滤器。如果another
过滤器为null
,则返回filter
。否则,返回一个用于合并一对non-null
过滤器的filter
。返回的过滤器实现了checkInput(FilterInfo)
方法如下:- 在
FilterInfo
上调用filter
以获取其status
; - 如果
status
为REJECTED
,则返回REJECTED
; - 调用
anotherFilter
以获取otherStatus
; - 如果
otherStatus
为REJECTED
,则返回REJECTED
; - 如果
status
或otherStatus
中有一个为ALLOWED
,则返回ALLOWED
, - 否则,返回
UNDECIDED
- 参数:
-
filter
- 一个过滤器 -
anotherFilter
- 要与过滤器合并的过滤器,可以为null
- 返回:
-
一个
ObjectInputFilter
,用于合并过滤器状态和另一个过滤器状态 - 自JDK版本:
- 17
- 在
-
rejectUndecidedClass
返回一个调用给定过滤器并将UNDECIDED
映射为REJECTED
的过滤器,对于类,有一些特殊情况,否则返回状态。如果类不是原始类且不是数组,则返回的状态为REJECTED
。如果类是原始类或数组类,则执行额外的检查;有关详细信息,请参见下面的列表。对象反序列化接受一个类,如果过滤器返回
UNDECIDED
。添加一个过滤器以拒绝未决结果的类,可以防止类通过过滤器。- 实现要求:
-
返回的过滤器实现了
checkInput(FilterInfo)
方法如下: - 参数:
-
filter
- 一个过滤器 - 返回:
-
一个
ObjectInputFilter
,将ObjectInputFilter.Status.UNDECIDED
状态映射为ObjectInputFilter.Status.REJECTED
,否则返回过滤器状态 - 自JDK版本:
- 17
-