对实体类进行序列化时,控制台有如下警告:
查看官方文档,对其描述如下:
为了提高序列化的安全性和健壮性,Java提供了可筛选序列化对象的传入流的序列化过滤机制。过滤器可以在反序列化输入类之前对其进行验证。
在java中,所谓的序列化即将对象的状态转换为字节流并存储到文件、数据库或发送到网络(当父类实现了标记型接口Serializable或Externalizable子接口,其子类也可序列化)。而反序列化则与上述过程相反,可类比为加密与解密的过程。
序列化用于许多领域,包括远程方法调用(RMI),用于进程间通信(IPC)协议的自定义RMI(例如SpringHTTP调用程序),Java管理扩展(JMX)和Java消息服务(JMS))。当对象的序列化形式转换为该对象的副本时,该对象将反序列化。因此确保此转换的安全性很重要。反序列化的类的readObject方法可以包含自定义代码。可序列化的类(也称为“小工具类”)可以执行任意反射操作。
创建过滤器时,可以指定接受哪些类,拒绝哪些类。也可以在反序列化期间控制对象图(objectgraph)的大小和复杂度,以使对象图不会超出合理的限制。过滤器可以配置为属性,也可以通过编程实现。
除了创建过滤器之外,还可以采取以下措施来防止反序列化漏洞:
在JEP290中,Java序列化过滤机制的目标:
提供一种方法,将可以反序列化的类缩小为适合上下文的类集。
为反序列化期间的对象图大小和复杂度向过滤器提供指标,以验证正常的图形行为。
允许RMI导出的对象验证并调用期望的类。
实现序列化过滤器的方式:
基于模式的过滤器可以接受或拒绝特定的类,程序包或模块。他们可以限制数组大小,图形深度,总引用和流大小(通过黑名单和白名单实现)。而自定义过滤器可以特定于每个ObjectInputStream,在单个输入流或流程中的所有流上设置自定义过滤器。
基于模式的过滤器有一系列模式。每个模式都与流中的类名或资源限定名匹配。可以将基于类的资源限制模式组合在一个过滤器字符串中,每种模式都用分号(;)分隔。
创建此过滤器时,需要注意:
模式之间需要使用分号隔开。
pattern1.*;pattern2.*空格也是模式的一部分。
规则需要放在字符串的第一位。
模式前添加感叹号(!),即为拒绝模式匹配的类。
!pattern1.*;pattern2.*使用通配符(*)以一种模式表示未指定的类.
将基于模式的过滤器定义为一个程序的系统属性,系统属性取代安全属性值。jdk.serialFilter可在命令行中定义system属性。
限制单个程序的资源使用,如下所示:
java-Djdk.serialFilter=maxarray=100000;maxdepth=20;maxrefs=500com.example.test.Application在所有程序中定义基于模式的过滤器将基于模式的过滤器定义为所有程序的系统属性,系统属性取代安全属性值
创建一个全局应用的基于模式的类过滤器。例如,模式可以是类名或带有通配符的包。
过滤器拒绝包(!example.somepackage.SomeClass)中的一个类,并接受包中的所有其他类,如下所示:
jdk.serialFilter=!example.somepackage.SomeClass;example.somepackage.*;如果要拒绝包(!example.somepackage.SomeClass)中的所有类,需要添加(!*):
jdk.serialFilter=!example.somepackage.SomeClass;example.somepackage.*;!*定义资源限制过滤器资源过滤器限制了对象图的复杂性和大小。也可以为以下参数创建过滤器,以控制每个程序的资源使用情况:
基于模式的过滤器用于简单的接受或拒绝,这些过滤器有一些限制:
自定义过滤器是在程序中指定的过滤器。它们在流程中的单个流或所有流上设置。可将自定义过滤器实现为模式,方法,lambda表达式或类。
设置一个自定义过滤器ObjectInputStream,如果要对每个流使用相同的过滤器,则需要设置一个进程范围的过滤器。如果ObjectInputStream没有为其定义过滤器,则将调用全程过滤器。
在对流进行解码时,将发生以下操作:
当流的输入不受信任并且过滤器具有一组有限的要强制执行的类或约束时,可以在单个ObjectInputStream上设置过滤器。例如,可以确保流仅包含数字,字符串和其他程序指定的类型。
自定义过滤器是使用设置setObjectInputFilter方法。必须先设置自定义过滤器,然后才能从流中读取对象。
LocalDateTimereadDateTime(InputStreamis)throwsIOException{try(ObjectInputStreamois=newObjectInputStream(is)){ois.setObjectInputFilter(FilterClass::dateTimeFilter);return(LocalDateTime)ois.readObject();}catch(ClassNotFoundExceptionex){IOExceptionioe=newStreamCorruptedException("classmissing");ioe.initCause(ex);throwioe;}}设置流程范围的自定义过滤器该过滤器适用于ObjectInputStream的每次使用,除非它在特定的流上被覆盖。如果可以确定整个应用程序所需的每种类型和条件,则过滤器可以允许这些类型和条件,而拒绝其余的类型和条件。通常,过程范围的筛选器用于拒绝特定的类或程序包,或限制数组大小,对象图的深度或总大小。
使用ObjectInputFilter.Config类的方法设置一次进程范围的过滤器。过滤器可以是类,lambda表达式,方法引用或模式的实例。
ObjectInputFilterfilter=...ObjectInputFilter.Config.setSerialFilter(filter);在以下示例中,使用lambda表达式设置进程范围的过滤器。
ObjectInputFilter.Config.setSerialFilter(info->info.depth()>10Status.REJECTED:Status.UNDECIDED);在下面的示例中,通过使用方法引用来设置全过程过滤器:
ObjectInputFilter.Config.setSerialFilter(FilterClass::dateTimeFilter);使用模式设置自定义过滤器通过使用ObjectInputFilter.Config.createFilter方法来创建基于模式的自定义过滤器,以方便简单使用。可以将基于模式的过滤器创建为系统属性或安全性属性。将基于模式的过滤器实现为方法或lambda表达式可提供更大的灵活性。
过滤器模式可以接受或拒绝特定的类,包,模块,并且可以限制数组大小,对象图的深度,总引用和流大小。模式不能匹配类的父类或接口。
在以下示例中,过滤器允许example.File和拒绝example.Directory类。
ObjectInputFilterfilesOnlyFilter=ObjectInputFilter.Config.createFilter("example.File;!example.Directory");此示例仅允许example.File。其他所有类均被拒绝。
ObjectInputFilterfilesOnlyFilter=ObjectInputFilter.Config.createFilter("example.File;!*");将自定义过滤器设置为类可以将自定义过滤器实现为实现java.io.ObjectInputFilter接口的类,lambda表达式或方法。
过滤器通常是无状态的,仅对输入参数执行检查。但是可以实现一个过滤器,例如,维持对checkInput方法的调用之间的状态以对流中Artifacts的计数。
在下面的示例中,FilterNumber该类允许作为该类实例的任何对象,Number并拒绝所有其他对象。
classFilterNumberimplementsObjectInputFilter{publicStatuscheckInput(FilterInfofilterInfo){Class<>clazz=filterInfo.serialClass();if(clazz!=null){return(Number.class.isAssignableFrom(clazz))Status.ALLOWED:Status.REJECTED;}returnStatus.UNDECIDED;}}在示例中:
使用方法引用代替内联lambda表达式。
publicclassFilterClass{staticObjectInputFilter.StatusdateTimeFilter(ObjectInputFilter.FilterInfoinfo){Class<>serialClass=info.serialClass();if(serialClass!=null){returnserialClass.getPackageName().equals("java.time")ObjectInputFilter.Status.ALLOWED:ObjectInputFilter.Status.REJECTED;}returnObjectInputFilter.Status.UNDECIDED;}}示例:过滤java.base模块中的类
这个自定义过滤器(也实现为方法)仅允许在JDK的基本模块中找到的类。本示例适用于JDK9和更高版本。
staticObjectInputFilter.StatusbaseFilter(ObjectInputFilter.FilterInfoinfo){Class<>serialClass=info.serialClass();if(serialClass!=null){returnserialClass.getModule().getName().equals("java.base") ObjectInputFilter.Status.ALLOWED :ObjectInputFilter.Status.REJECTED;}returnObjectInputFilter.Status.UNDECIDED;}内置过滤器Java远程方法调用(RMI)注册表,RMI分布式垃圾收集器和Java管理扩展(JMX)都具有JDK中包含的过滤器。
注意:这些内置过滤器只能当做起点。编辑sun.rmi.transport.dgcFilter系统属性以配置黑名单和/或扩展白名单以为分布式垃圾收集器添加附加保护。为了保护整个程序,请将模式添加到jdk.serialFilter全局系统属性中,以提供其他没有自定义过滤器的序列化用户的保护。
RMI分布式垃圾收集器具有一个内置的白名单过滤器,可以接受一组有限的类。它包括的情况下java.rmi.server.ObjID,java.rmi.server.UID,java.rmi.dgc.VMID,和java.rmi.dgc.Lease类。
内置过滤器包括尺寸限制:
maxarray=1000000,maxdepth=20通过使用sun.rmi.transport.dgcFilter模式的system属性定义过滤器来取代内置过滤器。如果过滤器接受传递给过滤器的类,或者拒绝类或大小,则不会调用内置过滤器。如果取代的过滤器不接受或拒绝任何内容,则将调用内置过滤器。
注意:这些内置过滤器只能当做起点。编辑jmx.remote.rmi.server.serial.filter.pattern管理属性以配置黑名单和/或扩展白名单以为JMX添加其他保护。为了保护整个应用程序,请将模式添加到jdk.serialFilter全局系统属性中,以提供其他没有自定义过滤器的序列化用户的保护。
JMX具有一个内置过滤器,用于限制允许通过RMI作为反序列化参数发送到服务器的一组类。默认情况下,该过滤器是禁用的。要启用过滤器,请jmx.remote.rmi.server.serial.filter.pattern使用模式定义管理属性。
该模式必须包括允许通过RMI作为参数发送到服务器的类型以及它们所依赖的所有类型javax.management.ObjectName以及java.rmi.MarshalledObject类型。例如,要将允许的类集限制为OpenMBean类型及其依赖的类型,请在management.properties文件中添加以下行。
com.sun.management.jmxremote.serial.filter.pattern=java.lang.*;java.math.BigInteger;java.math.BigDecimal;java.util.*;javax.management.openmbean.*;javax.management.ObjectName;java.rmi.MarshalledObject;!*记录过滤器操作日志记录用来记录对序列化筛选器的调用的初始化。在配置白名单和黑名单时,可以使用日志输出作为诊断工具来查看正在反序列化的内容,并确认设置。
启用日志记录后,过滤器操作将记录到java.io.serialization记录器中。
启用序列化过滤器日志记录,编辑$JDK_HOME/conf/logging.properties文件。