网易首页 > 网易号 > 正文 申请入驻

Fastjson到底做错了什么?为什么会被频繁爆出漏洞?

0
分享至

作者:HollisChuang

Fastjson大家一定都不陌生,这是阿里巴巴的开源一个JSON解析库,通常被用于将Java Bean和JSON 字符串之间进行转换。

前段时间,fastjson被爆出过多次存在漏洞,很多文章报道了这件事儿,并且给出了升级建议。

但是作为一个开发者,我更关注的是他为什么会频繁被爆漏洞?于是我带着疑惑,去看了下fastjson的releaseNote以及部分源代码。

最终发现,这其实和fastjson中的一个AutoType特性有关。

从2019年7月份发布的v1.2.59一直到2020年6月份发布的 v1.2.71 ,每个版本的升级中都有关于AutoType的升级。

下面是fastjson的官方releaseNotes 中,几次关于AutoType的重要升级:

1.2.59发布,增强AutoType打开时的安全性 fastjson 1.2.60发布,增加了AutoType黑名单,修复拒绝服务安全问题 fastjson 1.2.61发布,增加AutoType安全黑名单 fastjson 1.2.62发布,增加AutoType黑名单、增强日期反序列化和JSONPath fastjson 1.2.66发布,Bug修复安全加固,并且做安全加固,补充了AutoType黑名单 fastjson 1.2.67发布,Bug修复安全加固,补充了AutoType黑名单 fastjson 1.2.68发布,支持GEOJSON,补充了AutoType黑名单。(引入一个safeMode的配置,配置safeMode后,无论白名单和黑名单,都不支持autoType。) fastjson 1.2.69发布,修复新发现高危AutoType开关绕过安全漏洞,补充了AutoType黑名单 fastjson 1.2.70发布,提升兼容性,补充了AutoType黑名单

甚至在fastjson的开源库中,有一个Issue是建议作者提供不带autoType的版本:

那么,什么是AutoType?为什么fastjson要引入AutoType?为什么AutoType会导致安全漏洞呢?本文就来深入分析一下。

AutoType 何方神圣?

fastjson的主要功能就是将Java Bean序列化成JSON字符串,这样得到字符串之后就可以通过数据库等方式进行持久化了。

但是,fastjson在序列化以及反序列化的过程中并没有使用Java自带的序列化机制,而是自定义了一套机制。

其实,对于JSON框架来说,想要把一个Java对象转换成字符串,可以有两种选择:

  • 1、基于属性
  • 2、基于setter/getter

而我们所常用的JSON序列化框架中,FastJson和jackson在把对象序列化成json字符串的时候,是通过遍历出该类中的所有getter方法进行的。Gson并不是这么做的,他是通过反射遍历该类中的所有属性,并把其值序列化成json。

假设我们有以下一个Java类:

class Store { private String name; private Fruit fruit; public String getName() { return name; } public void setName(String name) { this.name = name; } public Fruit getFruit() { return fruit; } public void setFruit(Fruit fruit) { this.fruit = fruit; }}interface Fruit {}class Apple implements Fruit { private BigDecimal price; //省略 setter/getter、toString等}

当我们要对他进行序列化的时候,fastjson会扫描其中的getter方法,即找到getName和getFruit,这时候就会将name和fruit两个字段的值序列化到JSON字符串中。

那么问题来了,我们上面的定义的Fruit只是一个接口,序列化的时候fastjson能够把属性值正确序列化出来吗?如果可以的话,那么反序列化的时候,fastjson会把这个fruit反序列化成什么类型呢?

我们尝试着验证一下,基于(fastjson v 1.2.68):

Store store = new Store();store.setName("Hollis");Apple apple = new Apple();apple.setPrice(new BigDecimal(0.5));store.setFruit(apple);String jsonString = JSON.toJSONString(store);System.out.println("toJSONString : " + jsonString);

以上代码比较简单,我们创建了一个store,为他指定了名称,并且创建了一个Fruit的子类型Apple,然后将这个store使用JSON.toJSONString进行序列化,可以得到以下JSON内容:

toJSONString : {"fruit":{"price":0.5},"name":"Hollis"}

那么,这个fruit的类型到底是什么呢,能否反序列化成Apple呢?我们再来执行以下代码:

Store newStore = JSON.parseObject(jsonString, Store.class);System.out.println("parseObject : " + newStore);Apple newApple = (Apple)newStore.getFruit();System.out.println("getFruit : " + newApple);

执行结果如下:

toJSONString : {"fruit":{"price":0.5},"name":"Hollis"}parseObject : Store{name='Hollis', fruit={}}Exception in thread "main" java.lang.ClassCastException: com.hollis.lab.fastjson.test.$Proxy0 cannot be cast to com.hollis.lab.fastjson.test.Appleat com.hollis.lab.fastjson.test.FastJsonTest.main(FastJsonTest.java:26)

可以看到,在将store反序列化之后,我们尝试将Fruit转换成Apple,但是抛出了异常,尝试直接转换成Fruit则不会报错,如:

Fruit newFruit = newStore.getFruit();System.out.println("getFruit : " + newFruit);

以上现象,我们知道,当一个类中包含了一个接口(或抽象类)的时候,在使用fastjson进行序列化的时候,会将子类型抹去,只保留接口(抽象类)的类型,使得反序列化时无法拿到原始类型。

那么有什么办法解决这个问题呢,fastjson引入了AutoType,即在序列化的时候,把原始类型记录下来。

使用方法是通过SerializerFeature.WriteClassName进行标记,即将上述代码中的

String jsonString = JSON.toJSONString(store);

修改成:

String jsonString = JSON.toJSONString(store,SerializerFeature.WriteClassName);

即可,以上代码,输出结果如下:

System.out.println("toJSONString : " + jsonString);{ "@type":"com.hollis.lab.fastjson.test.Store", "fruit":{ "@type":"com.hollis.lab.fastjson.test.Apple", "price":0.5 }, "name":"Hollis"}

可以看到,使用SerializerFeature.WriteClassName进行标记后,JSON字符串中多出了一个@type字段,标注了类对应的原始类型,方便在反序列化的时候定位到具体类型

如上,将序列化后的字符串在反序列化,既可以顺利的拿到一个Apple类型,整体输出内容:

toJSONString : {"@type":"com.hollis.lab.fastjson.test.Store","fruit":{"@type":"com.hollis.lab.fastjson.test.Apple","price":0.5},"name":"Hollis"}parseObject : Store{name='Hollis', fruit=Apple{price=0.5}}getFruit : Apple{price=0.5}

这就是AutoType,以及fastjson中引入AutoType的原因。

但是,也正是这个特性,因为在功能设计之初在安全方面考虑的不够周全,也给后续fastjson使用者带来了无尽的痛苦

AutoType 何错之有?

因为有了autoType功能,那么fastjson在对JSON字符串进行反序列化的时候,就会读取@type到内容,试图把JSON内容反序列化成这个对象,并且会调用这个类的setter方法。

那么就可以利用这个特性,自己构造一个JSON字符串,并且使用@type指定一个自己想要使用的攻击类库。

举个例子,黑客比较常用的攻击类库是com.sun.rowset.JdbcRowSetImpl,这是sun官方提供的一个类库,这个类的dataSourceName支持传入一个rmi的源,当解析这个uri的时候,就会支持rmi远程调用,去指定的rmi地址中去调用方法。

而fastjson在反序列化时会调用目标类的setter方法,那么如果黑客在JdbcRowSetImpl的dataSourceName中设置了一个想要执行的命令,那么就会导致很严重的后果。

如通过以下方式定一个JSON串,即可实现远程命令执行(在早期版本中,新版本中JdbcRowSetImpl已经被加了黑名单)

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true}

这就是所谓的远程命令执行漏洞,即利用漏洞入侵到目标服务器,通过服务器执行命令。

在早期的fastjson版本中(v1.2.25 之前),因为AutoType是默认开启的,并且也没有什么限制,可以说是裸着的。

从v1.2.25开始,fastjson默认关闭了autotype支持,并且加入了checkAutotype,加入了黑名单+白名单来防御autotype开启的情况。

但是,也是从这个时候开始,黑客和fastjson作者之间的博弈就开始了。

因为fastjson默认关闭了autotype支持,并且做了黑白名单的校验,所以攻击方向就转变成了"如何绕过checkAutotype"。

下面就来细数一下各个版本的fastjson中存在的漏洞以及攻击原理,由于篇幅限制,这里并不会讲解的特别细节,如果大家感兴趣我后面可以单独写一篇文章讲讲细节。下面的内容主要是提供一些思路,目的是说明写代码的时候注意安全性的重要性。

绕过checkAutotype,黑客与fastjson的博弈

在fastjson v1.2.41 之前,在checkAutotype的代码中,会先进行黑白名单的过滤,如果要反序列化的类不在黑白名单中,那么才会对目标类进行反序列化。

但是在加载的过程中,fastjson有一段特殊的处理,那就是在具体加载类的时候会去掉className前后的L和;,形如Lcom.lang.Thread;。

而黑白名单又是通过startWith检测的,那么黑客只要在自己想要使用的攻击类库前后加上L和;就可以绕过黑白名单的检查了,也不耽误被fastjson正常加载。

如Lcom.sun.rowset.JdbcRowSetImpl;,会先通过白名单校验,然后fastjson在加载类的时候会去掉前后的L和,变成了com.sun.rowset.JdbcRowSetImpl`。

为了避免被攻击,在之后的 v1.2.42版本中,在进行黑白名单检测的时候,fastjson先判断目标类的类名的前后是不是L和;,如果是的话,就截取掉前后的L和;再进行黑白名单的校验。

看似解决了问题,但是黑客发现了这个规则之后,就在攻击时在目标类前后双写LL和;;,这样再被截取之后还是可以绕过检测。如LLcom.sun.rowset.JdbcRowSetImpl;;

魔高一尺,道高一丈。在 v1.2.43中,fastjson这次在黑白名单判断之前,增加了一个是否以LL未开头的判断,如果目标类以LL开头,那么就直接抛异常,于是就又短暂的修复了这个漏洞。

黑客在L和;这里走不通了,于是想办法从其他地方下手,因为fastjson在加载类的时候,不只对L和;这样的类进行特殊处理,还对[也被特殊处理了。

同样的攻击手段,在目标类前面添加[,v1.2.43以前的所有版本又沦陷了。

于是,在 v1.2.44版本中,fastjson的作者做了更加严格的要求,只要目标类以[开头或者以;结尾,都直接抛异常。也就解决了 v1.2.43及历史版本中发现的bug。

在之后的几个版本中,黑客的主要的攻击方式就是绕过黑名单了,而fastjson也在不断的完善自己的黑名单。

autoType不开启也能被攻击?

但是好景不长,在升级到 v1.2.47 版本时,黑客再次找到了办法来攻击。而且这个攻击只有在autoType关闭的时候才生效。

是不是很奇怪,autoType不开启反而会被攻击。

因为**在fastjson中有一个全局缓存,在类加载的时候,如果autotype没开启,会先尝试从缓存中获取类,如果缓存中有,则直接返回。**黑客正是利用这里机制进行了攻击。

黑客先想办法把一个类加到缓存中,然后再次执行的时候就可以绕过黑白名单检测了,多么聪明的手段。

首先想要把一个黑名单中的类加到缓存中,需要使用一个不在黑名单中的类,这个类就是java.lang.Class

java.lang.Class类对应的deserializer为MiscCodec,反序列化时会取json串中的val值并加载这个val对应的类。

如果fastjson cache为true,就会缓存这个val对应的class到全局缓存中

如果再次加载val名称的类,并且autotype没开启,下一步就是会尝试从全局缓存中获取这个class,进而进行攻击。

所以,黑客只需要把攻击类伪装以下就行了,如下格式:

{"@type": "java.lang.Class","val": "com.sun.rowset.JdbcRowSetImpl"}

于是在 v1.2.48中,fastjson修复了这个bug,在MiscCodec中,处理Class类的地方,设置了fastjson cache为false,这样攻击类就不会被缓存了,也就不会被获取到了。

在之后的多个版本中,黑客与fastjson又继续一直都在绕过黑名单、添加黑名单中进行周旋。

直到后来,黑客在 v1.2.68之前的版本中又发现了一个新的漏洞利用方式。

利用异常进行攻击

在fastjson中, 如果,@type 指定的类为 Throwable 的子类,那对应的反序列化处理类就会使用到 ThrowableDeserializer

而在ThrowableDeserializer#deserialze的方法中,当有一个字段的key也是 @type时,就会把这个 value 当做类名,然后进行一次 checkAutoType 检测。

并且指定了expectClass为Throwable.class,但是在checkAutoType中,有这样一约定,那就是如果指定了expectClass ,那么也会通过校验。

因为fastjson在反序列化的时候会尝试执行里面的getter方法,而Exception类中都有一个getMessage方法。

黑客只需要自定义一个异常,并且重写其getMessage就达到了攻击的目的。

这个漏洞就是6月份全网疯传的那个"严重漏洞",使得很多开发者不得不升级到新版本。

这个漏洞在 v1.2.69中被修复,主要修复方式是对于需要过滤掉的expectClass进行了修改,新增了4个新的类,并且将原来的Class类型的判断修改为hash的判断。

其实,根据fastjson的官方文档介绍,即使不升级到新版,在v1.2.68中也可以规避掉这个问题,那就是使用safeMode

AutoType 安全模式?

可以看到,这些漏洞的利用几乎都是围绕AutoType来的,于是,在 v1.2.68版本中,引入了safeMode,配置safeMode后,无论白名单和黑名单,都不支持autoType,可一定程度上缓解反序列化Gadgets类变种攻击。

设置了safeMode后,@type 字段不再生效,即当解析形如{"@type": "com.java.class"}的JSON串时,将不再反序列化出对应的类。

开启safeMode方式如下:

ParserConfig.getGlobalInstance().setSafeMode(true);

如在本文的最开始的代码示例中,使用以上代码开启safeMode模式,执行代码,会得到以下异常:

Exception in thread "main" com.alibaba.fastjson.JSONException: safeMode not support autoType : com.hollis.lab.fastjson.test.Appleat com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:1244)

但是值得注意的是,使用这个功能,fastjson会直接禁用autoType功能,即在checkAutoType方法中,直接抛出一个异常。

后话

目前fastjson已经发布到了 v1.2.72版本,历史版本中存在的已知问题在新版本中均已修复。

开发者可以将自己项目中使用的fastjson升级到最新版,并且如果代码中不需要用到AutoType的话,可以考虑使用safeMode,但是要评估下对历史代码的影响。

因为fastjson自己定义了序列化工具类,并且使用asm技术避免反射、使用缓存、并且做了很多算法优化等方式,大大提升了序列化及反序列化的效率。

之前有网友对比过:

当然,快的同时也带来了一些安全性问题,这是不可否认的。

最后,其实我还想说几句,虽然fastjson是阿里巴巴开源出来的,但是据我所知,这个项目大部分时间都是其作者温少一个人在靠业余时间维护的。

知乎上有网友说:"温少几乎凭一己之力撑起了一个被广泛使用JSON库,而其他库几乎都是靠一整个团队,就凭这一点,温少作为“初心不改的阿里初代开源人”,当之无愧。"

其实,关于fastjson漏洞的问题,阿里内部也有很多人诟病过,但是诟病之后大家更多的是给予理解包容

fastjson目前是国产类库中比较出名的一个,可以说是倍受关注,所以渐渐成了安全研究的重点,所以会有一些深度的漏洞被发现。就像温少自己说的那样:

"和发现漏洞相比,更糟糕的是有漏洞不知道被人利用。及时发现漏洞并升级版本修复是安全能力的一个体现。"

就在我写这篇文章的时候,在钉钉上问了温少一个问题,他竟然秒回,这令我很惊讶。因为那天是周末,周末钉钉可以做到秒回,这说明了什么?

他大概率是在利用自己的业余维护fastjson吧...

最后,知道了fastjson历史上很多漏洞产生的原因之后,其实对我自己来说,我是"更加敢用"fastjson了...

致敬fastjson!致敬安全研究者!致敬温少!

来源:掘金 链接:https://juejin.im/post/5f0283956fb9a07eb90cac4c

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
5换1!湖人掘金交易方案,约基奇身边最强大腿,辅佐东契奇冲冠

5换1!湖人掘金交易方案,约基奇身边最强大腿,辅佐东契奇冲冠

球毛鬼胎
2026-07-15 13:56:21
中国造CH-95在苏丹上空被首杀!

中国造CH-95在苏丹上空被首杀!

牲产队
2026-07-15 21:05:33
世界杯半决赛分析:阿根廷VS英格兰,阿根廷苟着,英格兰强势

世界杯半决赛分析:阿根廷VS英格兰,阿根廷苟着,英格兰强势

铁甲西奇
2026-07-15 12:38:28
宇树人形机器人G1“操刀”,为两头活猪完成手术,系全球首次!论文作者回应:项目研究重点在于人形机器人的仿人外形,而非特定的平台

宇树人形机器人G1“操刀”,为两头活猪完成手术,系全球首次!论文作者回应:项目研究重点在于人形机器人的仿人外形,而非特定的平台

每日经济新闻
2026-07-15 15:54:11
16岁女高中生每天玩手机到半夜,母亲洗衣发现硬块,翻开衣服崩溃

16岁女高中生每天玩手机到半夜,母亲洗衣发现硬块,翻开衣服崩溃

罪案洞察者
2025-07-09 11:19:55
国台办:祖国必须统一,也必然统一,这是任何人任何势力都无法阻挡的历史大势

国台办:祖国必须统一,也必然统一,这是任何人任何势力都无法阻挡的历史大势

环球网资讯
2026-07-15 11:04:15
西班牙一门西蒙7战6零封二门三门坐着英超西甲金手套

西班牙一门西蒙7战6零封二门三门坐着英超西甲金手套

纵拥千千晚星
2026-07-15 17:57:57
1270万大学生毕业,复旦教授:再发达的国家,也用不到这么多大学生

1270万大学生毕业,复旦教授:再发达的国家,也用不到这么多大学生

平说财经
2026-07-14 13:58:25
太逆天!又一个史诗级天赋怪出现,静态天赋超越文班亚马!

太逆天!又一个史诗级天赋怪出现,静态天赋超越文班亚马!

宝哥精彩赛事
2026-07-15 11:45:11
梅西冲上世界杯金球奖赔率第一,贝林厄姆第二,亚马尔第四!

梅西冲上世界杯金球奖赔率第一,贝林厄姆第二,亚马尔第四!

李喜林篮球绝杀
2026-07-15 17:07:08
他是广东著名主持,却经历父母去世,女友病逝,今58岁单身无儿女

他是广东著名主持,却经历父母去世,女友病逝,今58岁单身无儿女

星娱叨叨社
2026-07-15 15:00:37
熬过低谷的人,早已悄悄换了一副灵魂

熬过低谷的人,早已悄悄换了一副灵魂

青苹果sht
2026-07-15 05:11:16
美国国务院公开出来喊话了!要求赶快放人!

美国国务院公开出来喊话了!要求赶快放人!

阿振观点
2026-07-15 14:18:05
海关总署:在全球高温多发的情况下,我国空调、电扇、冰箱等“清凉”家电合计出口了1079.1亿元,为全球消费者送去了阵阵“清凉”

海关总署:在全球高温多发的情况下,我国空调、电扇、冰箱等“清凉”家电合计出口了1079.1亿元,为全球消费者送去了阵阵“清凉”

政知新媒体
2026-07-14 13:41:53
何超欣西班牙办27岁生日宴,黑色抹胸裙出镜,巨型红玫瑰很奢华

何超欣西班牙办27岁生日宴,黑色抹胸裙出镜,巨型红玫瑰很奢华

仙味少女心
2026-07-14 22:38:14
重大利好,国务院批复!60万亿元→

重大利好,国务院批复!60万亿元→

新理财杂志
2026-07-15 18:56:59
朝鲜总理刚离京,72小时不到,中方直奔平壤,给未来几十年定调

朝鲜总理刚离京,72小时不到,中方直奔平壤,给未来几十年定调

时光流转追梦人
2026-07-15 23:27:44
哈兰德世界杯后十指紧扣女友赴意大利时装秀 全白造型抢镜

哈兰德世界杯后十指紧扣女友赴意大利时装秀 全白造型抢镜

环球趣闻分享
2026-07-15 16:26:57
法国遗憾出局!世界杯3大玄学魔咒:3强争霸,冠军均指向同一队

法国遗憾出局!世界杯3大玄学魔咒:3强争霸,冠军均指向同一队

伴史缘
2026-07-15 10:11:34
王晶点评周星驰新片:是其近十年拍得最好的作品,武戏不输《少林足球》,但文戏缺少吴孟达、黄一飞等黄金配角略显不足

王晶点评周星驰新片:是其近十年拍得最好的作品,武戏不输《少林足球》,但文戏缺少吴孟达、黄一飞等黄金配角略显不足

极目新闻
2026-07-15 20:30:28
2026-07-15 23:56:49
Java架构技术
Java架构技术
专注于Java领域优质技术
502文章数 14662关注度
往期回顾 全部

科技要闻

国行大突破!“Apple智能”已备案

头条要闻

美国要“彻底瓦解”国际刑事法院 日本慌了

头条要闻

美国要“彻底瓦解”国际刑事法院 日本慌了

体育要闻

世界杯两大巨星,加一起22岁

娱乐要闻

大S遗嘱曝光!S家拒不承认

财经要闻

梁文锋身家2400亿登顶全球AI首富

汽车要闻

爱玩会玩 小鹏MONA L03这次来势凶猛

态度原创

亲子
本地
旅游
公开课
军事航空

亲子要闻

小汽车乱丢车轮 #工程车玩具#儿童动画

本地新闻

一脚踢进宋朝?来开封解锁宋式快乐

旅游要闻

千亩红莲作舞台 一袭霓裳醉莲乡

公开课

李玫瑾:为什么性格比能力更重要?

军事要闻

美军称已恢复对伊朗的海上封锁

无障碍浏览 进入关怀版