FastJson autoType is not support


 

最近項目里用到了阿里巴巴的fastjson工具,遇到一些問題,記錄分享一下

github說明:

fastjson是阿里巴巴的開源JSON解析庫,它可以解析JSON格式的字符串,支持將Java Bean序列化為JSON字符串,也可以從JSON字符串反序列化到JavaBean。

使用:

添加maven依賴

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.54</version>
</dependency>

API使用

String text = JSON.toJSONString(obj); //序列化
VO vo = JSON.parseObject("{...}", VO.class); //反序列化

但有時候會出現

使用Redis 配置替換fastjson 反序列化報錯 com.alibaba.fastjson.JSONException: autoType is not support

解決方法是

在RedisSerializer<T>的實現類中添加白名單

//添加白名單
static {
ParserConfig.getGlobalInstance().addAccept("com.glz.oauthmanager");
}
復制代碼
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    private Class<T> clazz;

    //添加白名單
    static {
        ParserConfig.getGlobalInstance().addAccept("com.glz.oauthmanager");
    }

    public FastJsonRedisSerializer(Class<T> clazz) {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length <= 0) {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);
        return (T) JSON.parseObject(str, clazz);
    }

}
復制代碼

如果還是不行,官方還有其他解決辦法

逆風的方向,更適合飛翔!

https://www.cnblogs.com/nisiweiLIQIYONG/p/10843581.html

 

由於項目使用的是fastjson,也無法換成其他的序列化框架,所以研究了一下他對泛型序列化和反序列化的支持能力,最終解決了這個問題。

要達成的目標

我的封裝方式屬於通用封裝,我要達到的目標是如下的使用方式:

放入數據:

 Map<String, OffheapDTO> mapxx = new HashMap<>();
        mapxx.put("1",new OffheapDTO().create()); mapxx.put("2",new OffheapDTO().create()); mapxx.put("3",new OffheapDTO().create()); mapxx.put("4",new OffheapDTO().create()); mapxx.put("5",new OffheapDTO().create()); offheapCacheWrapper.putMap("maptest", mapxx); 

獲取數據:

 Map<String, OffheapDTO> resultmap = offheapCacheWrapper.queryMap("cachemap")

OffheapDTO對象的定義方式如下:

class OffheapDTO implements Serializable, Comparable{
    private String name; private String address; private String mark; private int order; //省略getset }

也就是我可以隨意的把任何對象進行序列化操作,然后可以隨意的把任何已經序列化的對象,反序列化回來。

第一版代碼代碼,未中其意

 
putMap方法代碼如下:
    public <T> void putMap(String key, T value, long expireSeconds) {
        try {
            EntityWrapper<T> entityWrapper = new EntityWrapper<>().create(key, value, expireSeconds); initMapContainer(OffheapCacheConst.MAP_CONTAINER_FOR_STRING).put(key, JSON.toJSONString(entityWrapper)); } catch (Exception ex) { logger.error(OffheapCacheConst.PACKAGE_CONTAINER + "putMapSingle with expires exception:", ex); throw ex; }
    }

queryMap方法代碼如下:

    public <T> T queryMap(String key) {
        try { Object result = initMapContainer(OffheapCacheConst.MAP_CONTAINER_FOR_STRING).get(key); if(result == null){ return null; } //反序列化出entityWrapper EntityWrapper entityWrapper = JSON.parseObject(result.toString()); return (T)entityWrapper.getEntity(); } catch (Exception ex) { logger.error(OffheapCacheConst.PACKAGE_CONTAINER + "queryMap exception:", ex); return null; }
    }

結果當我反序列化的時候,調用resultmap.get("1")的時候,提示我無法將jsonObject轉變成OffheapDTO. 調試進去發現,List對象里面裝載的仍然是jsonObject數據。初次嘗試失敗。

第二版代碼,苦盡甘來

之后翻看了百度,查閱了大量資料,然后看到了關於TypeReference的代碼,之后將queryMap方法修改如下:

    public <T> T queryMap(String key) {
        try { Object result = initMapContainer(OffheapCacheConst.MAP_CONTAINER_FOR_STRING).get(key); if(result == null){ return null; } //反序列化出entityWrapper EntityWrapper entityWrapper = JSON.parseObject(result.toString(),new TypeReference<EntityWrapper<T>>() {});
            return (T)entityWrapper.getEntity();
        } catch (Exception ex) { logger.error(OffheapCacheConst.PACKAGE_CONTAINER + "queryMap exception:", ex); return null; } } 

注意代碼中黃色部分。

然后當我再次進行反序列化的時候,我發現resultMap.get(“1”)已經可以拿到正常的OffheapDTO對象了。心中一喜,然后運行resultMap.get(“1”).getName(), 居然又報錯,提示無法將jsonObject轉變成OffheapDTO對象,發現原來存儲的字段,居然都是jsonObject類型。這次就有點慌了。

第三版代碼,驀然回首

不過想想fastjson這么成熟,定然有前人的輪子,所以就繼續查閱資料,終於查到了setAutoTypeSupport這個屬性,沒想到一試,居然解決了問題。

首先,程序啟動的時候,需要開啟這個屬性,至於這個屬性真正的意義,去翻閱fastjson文檔,我這里就不贅述了:

      //開啟fastjson autotype功能(不開啟,造成EntityWrapper<T>中的T無法正常解析)
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

然后,在序列化的時候,需要附帶上序列化的class名稱(黃色標記部分):

    public <T> void putMap(String key, T value, long expireSeconds) { try { EntityWrapper<T> entityWrapper = new EntityWrapper<>().create(key, value, expireSeconds); initMapContainer(OffheapCacheConst.MAP_CONTAINER_FOR_STRING).put(key, JSON.toJSONString(entityWrapper, SerializerFeature.WriteClassName));
        } catch (Exception ex) {
            logger.error(OffheapCacheConst.PACKAGE_CONTAINER + "putMapSingle with expires exception:", ex); throw ex; } }

最后,在反序列化的時候,利用TypeReference進行類型指定即可:

    public <T> T queryMap(String key) {
        try { Object result = initMapContainer(OffheapCacheConst.MAP_CONTAINER_FOR_STRING).get(key); if(result == null){ return null; } //反序列化出entityWrapper EntityWrapper entityWrapper = JSON.parseObject(result.toString(),new TypeReference<EntityWrapper<T>>() {});
            return (T)entityWrapper.getEntity();
        } catch (Exception ex) { logger.error(OffheapCacheConst.PACKAGE_CONTAINER + "queryMap exception:", ex); return null; } } 

這樣,無論你的類有多復雜,都可以搞定,比如像下面這樣的:

Map<String,List< OffheapDTO>> resultmap = offheapCacheWrapper.queryMap("maptest");

甚至這樣的:

List<Map<String,List<Set<OffheapDTO>>>> resultmap = offheapCacheWrapper.queryMap("maptest");

https://www.cnblogs.com/scy251147/p/9451879.html

 

#打開AutoType功能 在1.2.25之后的版本,以及所有的.sec01后綴版本中,autotype功能是受限的,和之前的版本不同,如果在升級的過程中遇到問題,可以通過以下方法配置。

一、添加autotype白名單

添加白名單有三種方式,三選一,如下:

1. 在代碼中配置

ParserConfig.getGlobalInstance().addAccept("com.taobao.pac.client.sdk.dataobject."); 

如果有多個包名前綴,分多次addAccept

2. 加上JVM啟動參數

    -Dfastjson.parser.autoTypeAccept=com.taobao.pac.client.sdk.dataobject.,com.cainiao. 

如果有多個包名前綴,用逗號隔開

3. 通過fastjson.properties文件配置。

在1.2.25/1.2.26版本支持通過類路徑的fastjson.properties文件來配置,配置方式如下:

fastjson.parser.autoTypeAccept=com.taobao.pac.client.sdk.dataobject.,com.cainiao. // 如果有多個包名前綴,用逗號隔開

二、打開autotype功能

如果通過配置白名單解決不了問題,可以選擇繼續打開autotype功能,fastjson在新版本中內置了多重防護,但是還是可能會存在一定風險。兩種方法打開autotype,二選一,如下:

1、JVM啟動參數

-Dfastjson.parser.autoTypeSupport=true

2、代碼中設置

ParserConfig.getGlobalInstance().setAutoTypeSupport(true); 

如果有使用非全局ParserConfig則用另外調用setAutoTypeSupport(true);

三、配置autoType黑名單

打開AutoType之后,是基於內置黑名單來實現安全的,但黑名單是窮舉不完的,如果發現了新的風險類,可以通過以下配置來增加黑名單:

1. 配置JVM啟動參數

-Dfastjson.parser.deny=xx.xxx
  • 這里的xx.xxx是包名前綴,如果有多個包名前綴,用逗號隔開

2. 通過fastjson.properties來配置

在1.2.25之后的版本支持通過類路徑的fastjson.properties文件來配置,配置方式如下:

-Dfastjson.parser.deny=xx.xxx 
  • 這里的xx.xxx是包名前綴,如果有多個包名前綴,用逗號隔開

3. 代碼中配置

ParserConfig.getGlobalInstance().addDeny("xx.xxx");
  • 這里的xx.xxx是包名前綴,如果有多個包名前綴,用逗號隔開

https://github.com/alibaba/fastjson/wiki/enable_autotype

 

 

使用Redis 配置替換fastjson 反序列化報錯 com.alibaba.fastjson.JSONException: autoType is not support

新建的GenericFastJson2JsonRedisSerializer里面添加白名

添加:

static {
        ParserConfig.getGlobalInstance().addAccept("com.xxx.xxx.bo");
        ParserConfig.getGlobalInstance().addAccept("com.xxx.xxx.redis");
    }
參考:https://blog.csdn.net/InnovationAD/article/details/84341687

https://www.cnblogs.com/xyj179/p/10095250.html

 

 

 

 

總結:在使用fastjson類序列化時,尤其是redis這個序列的支持,在類中要加保證一默認的構造方法,否則在進行反序列化時,fastjosn根據typeName進行反序列化,會發生該異常

 

 

安全升級公告

最近發現fastjson在1.2.24以及之前版本存在遠程代碼執行高危安全漏洞,為了保證系統安全,請升級到1.2.28/1.2.29/1.2.30/1.2.31或者更新版本。

1.2.29//1.2.30/1.2.31是在1.2.28版本上修復了一些大家升級過程中遇到的問題的版本,非安全問題,如果升級到1.2.25~1.2.28以及各種sec01版本的,也是沒有安全問題的。

1.2.25/1.2.26/1.2.27/1.2.28/1.2.29/1.2.30都是在升級的過程中修復不兼容問題發布的過度版本,如果你是在此之前升級到這些版本,不用因為這次的安全問題再次升級。

更新方法

1. Maven依賴配置更新

通過maven配置更新,使用最新版本,如下:

<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.31</version> </dependency> <!-- 1.2.41是最新的版本 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.41</version> </dependency> 

注意,版本號1.2.3~1.2.9版本都比1.2.31小,都是需要升級的。

2. 直接下載

常見問題

1. 升級遇到不兼容問題怎么辦?

1.2.28/1.2.29/1.2.30/1.2.31已經修復了絕大多數兼容問題,但是總會有一些特殊的用法導致不兼容,如果你遇到不兼容問題,通過 https://github.com/alibaba/fastjson/wiki/incompatible_change_list 查看不兼容問題,鏈接的后面提供了遇到不兼容問題之后的使用相應的sec01版本解決辦法。

2. 升級之后報錯autotype is not support

安全升級包禁用了部分autotype的功能,也就是"@type"這種指定類型的功能會被限制在一定范圍內使用。如果你使用場景中包括了這個功能,https://github.com/alibaba/fastjson/wiki/enable_autotype 這里有一個介紹如何添加白名單或者打開autotype功能。

3. 通過配置打開autotype之后是否存在安全漏洞

在1.2.28/1.2.29以及所有的.sec01版本中,有多重保護,但打開autotype之后仍會存在風險,不建議打開,而是使用一個較小范圍的白名單。打開autoType建議升級到最新版本1.2.37以上

4. Android環境使用是否需要升級

目前未發現漏洞對Android系統產生影響,在Android環境中使用不用升級。

5. 升級遇到問題希望提供支持怎么辦?

作者願意幫助大家一起解決問題,如果遇到文檔中沒說明到的問題,請通過如下方式聯系作者:

6. 有沒有漏洞利用詳情可以提供

為了保證更多用戶的安全,目前不適合擴散漏洞利用的細節

7. 是否有在WAF上檢測的辦法

檢測post內容中是否包含如下字符

"@type"

注意,為了減少誤報,包括雙引號

8. 檢測當前使用版本的是否有問題

  1. 通過maven dependency檢測
    如果是maven工程,在代碼根目錄下執行如下命令,如果有返回,就是需要升級
mvn dependency:tree | grep "com.alibaba.fastjson:" | grep -v sec01 | grep -v 1.2.25 | grep -v 1.2.26 | grep -v 1.2.27 | grep -v 1.2.28 | grep -v 1.2.29 | grep -v 1.2.30 | grep -v 1.2.31 
  1. 在lib目錄下執行如下腳本命令,可以判斷版本是否有問題
ls | grep fastjson | grep jar | grep -v sec01 | grep -v 1.2.25 | grep -v 1.2.26 | grep -v 1.2.27 | grep -v 1.2.28 | grep -v 1.2.29 | grep -v 1.2.30 | grep -v 1.2.31 

請注意,為了方便閱讀,加上了換行符,請使用時把上面的三行合並成一行。

  1. 看打開的文件中是否包含fastjson
sudo -u admin lsof -X | grep -v 1.2.25 | grep -v 1.2.26 | grep -v 1.2.27 | grep -v 1.2.28 | grep -v 1.2.29 | grep -v 1.2.30 | grep -v 1.2.31 

請注意,為了方便閱讀,加上了換行符,請使用時把上面的兩行合並成一行。另外通過lsof檢測,在tomcat某些場景是檢測不出來的,最好在lib目錄下用ls檢測(第2中方法)。

參考:



作者:xiaolyuh
鏈接:https://www.jianshu.com/p/a92ecc33fd0d
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM