java将序列化过滤器(ObjectInputFilter)与KeyClope适配器和Memcached一起使用
我正在使用SpringSecurity密钥斗篷适配器12.0.1和Memcached进行会话复制。从Memcached加载会话时,Keyclaok适配器中的类将被反序列化。类KeycloakSecurityContext
的read方法包含
DelegatingSerializationFilter.builder()
.addAllowedClass(KeycloakSecurityContext.class)
.setFilter(in);
。。。它为当前ObjectInputStream
设置一个ObjectFilter
我发现我必须将系统属性jdk.serialSetFilterAfterRead
设置为true,否则会抛出异常filter can not be set after an object has been read
,并且DelegatingSerializationFilter
抱怨无法设置对象筛选器。结果是根本没有应用任何对象筛选器,日志中会出现垃圾邮件警告
在应用jdk.serialSetFilterAfterRead
之后,我遇到了具有memcached属性的ObjectInputStream
包含的其他类没有从DelegatingSerializationFilter
设置为允许的类,例如:
org.springframework.security.web.savedrequest.DefaultSavedRequest
结果是这些类在序列化过程中被拒绝
所以我的问题是:有人知道如何配置对象过滤器以使序列化正常工作吗
# 1 楼答案
我知道这个问题是关于Memcached的,但我在这里使用Spring Session w/ Hazelcast时出现了完全相同的错误。我会把我发现的东西贴出来,以防对别人有帮助
基本上,Spring会话Hazelcast 2.4.2提供了
HazelcastSessionSerializer
。我以前使用的是2.3.2,它没有这个类,默认为Java序列化,在反序列化DefaultSavedRequest
时导致“拒绝”错误latest configuration example包含一些新行来设置此序列化程序:
这为我解决了这个问题
# 2 楼答案
在
KeycloakSecurityContext
和其他related classes中实现的代码包括在内,以缓解与CVECVE-2020-1714
相关的bug:该解决方案试图通过实现自定义^{} 来解决上述漏洞
如错误描述所示,Java序列化过滤机制背后的思想是防止反序列化漏洞,这些漏洞可能导致远程代码执行,从而导致应用程序出现安全问题
在许多情况下,例如在处理会话时,存储在下面示例中的会话中的多个对象被序列化,随后将一起反序列化,换句话说,它们将被写入相同的^{} ,然后从相同的^{} 读取
当应用
ObjectInputFilter
时,可以在多个级别上执行—流程、应用程序和特定ObjectInputStream
—只有满足配置的filter pattern的对象将被反序列化;取决于模式本身,其余部分要么被拒绝,要么将决策委托给process-wide filter if one exists请考虑下面的示例,其中^ {< 8CD> }、^ {CD9>}、^ {< CD10>}和^ {CD11>}是类,并且过滤器^ {< CD12>}应用在假设的对象输入流上,以顶部大理石图线表示,是应用过滤器后的荒漠化结果的下端:
此模式可以根据模块、包和/或单个类来定义,并且可以设置为
jdk.serialFilter
系统属性,或者通过编辑java.security
属性文件来设置您还可以创建自定义过滤器。它们使用
ObjectInputFilter
提供的API实现,并允许更细粒度的序列化控制,因为它们可以特定于特定的ObjectInputStream
请参阅相关的Oracle Serialization documentation了解更多信息
Keycloak
序列化筛选器使用实用程序类^{在提供的实现中,过滤器应用于^{} ^{} method 内部:
因此,如您所指出的,为了使其正常工作,有必要在运行程序时将
jdk.serialSetFilterAfterRead
系统属性定义为true
除非先前的非进程范围筛选器(请参阅Oracle documentation中标记为设置进程范围自定义筛选器的部分)已应用于^{,否则将始终应用此筛选器;它将由^{} 方法保证,它也是checked by the ^{} class :
换句话说,避免应用自定义筛选器的唯一方法是首先在目标
ObjectInputStream
上提供初始筛选器,这一次包含会话数据。如docs所示:这个初始过滤器的创建方式在很大程度上取决于实际处理反序列化功能的代码
在您的用例中,您可能正在使用
memcached-session-manager
,Github中的original version或某些more updated projects在正常用例中,} 中定义的代码处理
memcached-session-manager
中的会话主要由^{这个类使用^{} 来处理Java序列化内容
TranscoderService
反过来将这一责任委托给^{^{} 和相关类^{} 是这些接口的默认实现
请注意
JavaSerializationTranscoder
的deserializeAttributes
方法,它定义了会话反序列化的逻辑:如您所见,问题在于由输入字节数组表示的会话信息可能包含多个属性,并且所有属性都是从同一个}应用于此
ObjectInputStream
反序列化的。一旦Keycloak
{ObjectInputStream
,如您所示,它将拒绝筛选器不允许的其余类。原因是DelegatingSerializationFilter
{a24}是正在构造的过滤器模式的最终!*
,不包括t以外的所有内容他明确地提供了基于类和文本的模式(以及java.util.*
类以允许集合)为了避免这个问题,请尝试提供您自己的
SessionAttributesTranscoder
实现,并包含一个类似于deserializeAttributes
的方法,但在构造的ObjectInputStream
上定义一个初始过滤器例如(请原谅,为了定义整个类,您可能可以以某种方式重用
JavaSerializationTranscoder
的代码):现在,定义一个自定义
TranscoderFactory
。这次让我们重用类JavaSerializationTranscoderFactory
的代码:将这些类与}配置属性提供一个方便的值,如docs中所示:
memcached-session-manager
中的其余库一起放在类路径上,并为transcoderFactoryClass
{我没有能力测试解决方案,尽管一个简单的测试似乎可以正常工作:
Person
和Address
两个简单的POJO。请注意定义Person
{请注意,过滤器的存在是为了防止安全缺陷:正如代码注释中所建议的,建议改进
fixDeserialization
中实现的逻辑,以某种方式限制会话应该包含的可能类,而不是像示例中那样使用通配符*
事实上,这个功能可以包含在
memcached-session-manager
库中,可能是通过定义某种配置属性serialFilter
,例如,应该向指定的底层Java反序列化机制提供哪个值(有效的过滤器模式)我创建了这个项目的一个分支:它仍然是一个WIP,但是请看this commit,我希望你能理解这个想法。完成后,我将try to pull a request到forked repo去
# 3 楼答案
发生了什么事
您的问题是memcached会话处理程序同时反序列化了一组不同的类。也就是说,在相同的
ObjectInputStream
中。这是因为您的(Tomcat?或任何应用程序服务器,无所谓)会话由许多不同的对象组成;因此memcached将它们逐个序列化到其存储中,然后以相同的方式将它们反序列化回应用程序中。这非常有效,直到您反序列化一个bitchy类的对象,KeycloakSecurityContext
,它引入了一个邪恶的过滤器😱 在你的ObjectInputStream
中Link to the evil class之后发生的事情是,允许使用keydove类,因此它一直被正确地反序列化,但现在神奇地不允许使用所有其他类,因为keydove通过在其过滤器末尾添加“!*”来排除所有类Link to the filter
现在你头痛得厉害😵 你在想“废话够多了,我该怎么解决这个问题?”。我明白,而且我头疼得厉害,继续读下去
解决方案
这个问题有多种解决方案(好消息!😀) 取决于你胆量有多大(😣) 去更改应用程序中的内容
正确的™ 解决方案是确保
KeycloakSecurityContext
对象(以及引入此类过滤器的任何其他类)在它们自己的流中反序列化,而不是在相同的公共流中。现在,我不是这个memcached会话处理程序方面的专家,所以我不知道您对整个会话反序列化过程的实际控制程度;但我很有信心,它应该是有可能侵入它如果由于某种原因,这是不可能的,那么您需要通过扩展类来重写
KeycloakSecurityContext
中的过滤器。如果你选择了这条路(飞,你们这些傻瓜😈) 你必须使用过滤器并决定要做什么。我认为这是最正确的方法™ 在本例中,是从KeycloakSecurityContext
中完全删除过滤器,然后在应用程序级别添加过滤器,在应用程序级别定义允许反序列化的所有类和包。 Link on how to do it另一种有点老套但更直接的方法是在类扩展
KeycloakSecurityContext
中的过滤器中添加所有相关类(说真的,不要这样做)好公民奖励积分
有些人甚至可能认为这是
memcached-session-manager
中的一个bug,或者说是一个缺失的功能。也许值得在那里打开一个话题,看看他们是怎么想的。 https://github.com/magro/memcached-session-manager现在是核心的东西
为无所事事的书呆子们深入实际地解释这个问题
考虑以下场景:您有两种类型的类:
A
和B
,两个类都是Serializable
类
A
是一个简单的、有点幼稚的类,在其readObject
方法中不添加任何自定义过滤器。在同一个ObjectInputStream
中,反序列化对象:a1, a2, a3, a4
。类A
的所有实例。 所有对象都反序列化正确,操耶!🐒相反,类
B
在其readObject
中有一个非常平均的过滤器,它只允许在当前ObjectInputStream
中反序列化相同类B
的对象现在我们读取流中的以下对象:
a1, b1, a2, b2
a1
反序列化正确,我们的{b1
反序列化正确,酷;但是b1
也是一个小婊子(作为类B
的一个实例),所以它为我们喜爱的流设置了一个新的过滤器,从现在起,类B是唯一允许反序列化的类a2
,它是未反序列化的(whaaat😩), 因为b1
,在上一步中,设置了一个不包含类a的过滤器,这是所有发生在同一个ObjectInputStream
实例中的情况