项目中使用了 fastjson 1.2.78,但这个版本存在安全漏洞,需要升级到 fastjson 2.0.32。
漏洞说明如下:
- fastison <=1.2.68 反序列化远程代码执行漏洞:fastjson 采用黑白名单的方法来防御反序列化漏洞,导致当黑客不断发掘新的反序列化Gadgets类时,在autoType关闭的情况下仍然可能可以绕过黑白名单防御机制,造成远程命令执行漏洞。影响版本 fastjson <=1.2.68 fastison sec版本 <=sec9 安全版本 fastison >=1.2.69 fastison sec版本>=sec10
- Fastison版本低于1.2.58远程代码执行漏洞:当应用或系统使用 Fastjson 对由用户可控的 JSON字符串数据进行解析时,将可能导致远程代码执行的危害。影响版本:1.2.58以下版本
- fastjson <=1.2.80 反序列化任意代码执行漏洞:fastison 己使用黑白名单用于防御反序列化漏洞,经研究该利用在特定条件下可绕过默认autoType关闭限制,攻击远程服务器,风险影响较大。影响版本:fastjsons1.2.80
但 fastjson 1.2.78 和 fastjson 2.0.32 在 PropertyNamingStrategy 使用上略有不同:
- fastjson 1.2.78 中 PropertyNamingStrategy 不适用于Map中的Key。官方说明:PropertyNamingStrategy_cn · alibaba/fastjson Wiki (github.com)
- fastjson 2.0.32 中 PropertyNamingStrategy 既适用于对象中的属性,也适用于Map中的key。
所以,诉求就是既要升级到 2.0.32 版本,也要保持 1.2.78 版本中 PropertyNamingStrategy 不适用于 Map 中的 Key 这一特性;解决方案如下:
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.NameFilter;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson2.util.BeanUtils;
import lombok.Data;
import org.junit.Test;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* FastjsonTest
*
* @author Zexin Li
* @date 2023-08-22 16:58
*/
public class FastjsonTest {
// ==========>>>>>>>>>> 1.2.78 版本写法 <<<<<<<<<<==========
private static final SerializeConfig serializeConfig = new SerializeConfig();
static {
serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
}
public void toJsonString_v1_2_78(Object obj) {
return JSONObject.toJSONString(obj, serializeConfig);
}
// ==========>>>>>>>>>> 2.0.32 版本写法 <<<<<<<<<<==========
private static class CamelCaseToUnderscoreFilter implements NameFilter {
static final CamelCaseToUnderscoreFilter INSTANCE = new CamelCaseToUnderscoreFilter();
@Override
public String process(Object object, String name, Object value) {
// 如果name在Map中,就原样显示
if (object instanceof Map) {
return name;
}
// 使用 PropertyNamingStrategy.SnakeCase 代码逻辑
return BeanUtils.fieldName(name, PropertyNamingStrategy.SnakeCase.name());
}
}
public void toJsonString_v2_0_32(Object obj) {
return JSONObject.toJSONString(testObj, CamelCaseToUnderscoreFilter.INSTANCE);
}
}
正文结束。
👇下面是实验代码。
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.NameFilter;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson2.util.BeanUtils;
import lombok.Data;
import org.junit.Test;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* FastjsonTest
*
* @author Zexin Li
* @date 2023-08-22 16:58
*/
public class FastjsonTest {
private static final SerializeConfig serializeConfig = new SerializeConfig();
private static final ParserConfig parserConfig = new ParserConfig();
static {
serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
/* ==========>>>>>>>>>> 区别1 <<<<<<<<<<==========
1.2.78 可以正常使用
2.0.32 ParserConfig 没有 propertyNamingStrategy 属性了
*/
// parserConfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
}
@Data
private static class TestObj {
private String KEY;
private String keyKey;
private Map<Object, Object> mapKey = new LinkedHashMap<>();
}
@Test
public void testMapToJsonString() {
Map<Object, Object> originMap = new LinkedHashMap<>();
originMap.put("KEY", "VALUE");
originMap.put("keyKey", "valueValue");
/* ==========>>>>>>>>>> 区别2 <<<<<<<<<<==========
toJsonString 结果对比
1.2.78 结果:{"KEY":"VALUE","keyKey":"valueValue"}
2.0.32 结果:{"k_e_y":"VALUE","key_key":"valueValue"}
*/
String jsonString = JSONObject.toJSONString(originMap, serializeConfig);
System.out.println("toJSONString result: " + jsonString);
}
@Test
public void testJsonStringToMap() {
/*
parseObject 结果对比
1.2.78 结果:{KEY=VALUE, keyKey=valueValue}
2.0.32 结果:{KEY=VALUE, keyKey=valueValue}
*/
String jsonString = "{\"KEY\":\"VALUE\",\"keyKey\":\"valueValue\"}";
LinkedHashMap<?, ?> newMap = JSONObject.parseObject(jsonString, LinkedHashMap.class, parserConfig);
System.out.println("parseObject result: " + newMap);
/*
parseObject 结果对比
1.2.78 结果:{k_e_y=VALUE, key_key=valueValue}
2.0.32 结果:{k_e_y=VALUE, key_key=valueValue}
*/
String jsonString2 = "{\"k_e_y\":\"VALUE\",\"key_key\":\"valueValue\"}";
LinkedHashMap<?, ?> newMap2 = JSONObject.parseObject(jsonString2, LinkedHashMap.class, parserConfig);
System.out.println("parseObject result: " + newMap2);
}
@Test
public void testObjToJsonString() {
TestObj originObj = new TestObj();
originObj.setKEY("VALUE");
originObj.setKeyKey("valueValue");
/*
toJsonString 结果对比
1.2.78 结果:{"k_e_y":"VALUE","key_key":"valueValue"}
2.0.32 结果:{"k_e_y":"VALUE","key_key":"valueValue"}
*/
String jsonString = JSONObject.toJSONString(originObj, serializeConfig);
System.out.println("toJSONString result: " + jsonString);
}
@Test
public void testJsonStringToObj() {
/*
parseObject 结果对比
1.2.78 结果:TestObj(KEY=VALUE, keyKey=valueValue)
2.0.32 结果:TestObj(KEY=VALUE, keyKey=valueValue)
*/
String jsonString = "{\"KEY\":\"VALUE\",\"keyKey\":\"valueValue\"}";
TestObj newObj = JSONObject.parseObject(jsonString, TestObj.class, parserConfig);
System.out.println("parseObject result: " + newObj);
/*
parseObject 结果对比
1.2.78 结果:TestObj(KEY=VALUE, keyKey=valueValue)
2.0.32 结果:TestObj(KEY=VALUE, keyKey=valueValue)
*/
String jsonString2 = "{\"k_e_y\":\"VALUE\",\"key_key\":\"valueValue\"}";
TestObj newObj2 = JSONObject.parseObject(jsonString2, TestObj.class, parserConfig);
System.out.println("parseObject result: " + newObj2);
}
// ======================= 优化方案 =======================
private static class CamelCaseToUnderscoreFilter implements NameFilter {
static final CamelCaseToUnderscoreFilter INSTANCE = new CamelCaseToUnderscoreFilter();
@Override
public String process(Object object, String name, Object value) {
// 如果name在Map中,就原样显示
if (object instanceof Map) {
return name;
}
// 使用 PropertyNamingStrategy.SnakeCase 代码逻辑
return BeanUtils.fieldName(name, PropertyNamingStrategy.SnakeCase.name());
}
}
@Test
public void testMapToJsonString_FixBug() {
Map<Object, Object> originMap = new LinkedHashMap<>();
originMap.put("KEY", "VALUE");
originMap.put("keyKey", "valueValue");
originMap.put("KeyKey", "ValueValue");
TestObj testObj = new TestObj();
testObj.setMapKey(originMap);
testObj.setKEY("VALUE");
testObj.setKeyKey("valueValue");
/*
1.2.78 结果:{"k_e_y":"VALUE","key_key":"valueValue","map_key":{"KEY":"VALUE","keyKey":"valueValue","KeyKey":"ValueValue"}}
*/
String jsonString_1_2_78 = JSONObject.toJSONString(testObj, serializeConfig);
System.out.println("fastjson 1.2.78 toJSONString result: " + jsonString_1_2_78);
/*
2.0.32 结果:{"k_e_y":"VALUE","key_key":"valueValue","map_key":{"KEY":"VALUE","keyKey":"valueValue","KeyKey":"ValueValue"}}
*/
String jsonString_2_0_32 = JSONObject.toJSONString(testObj, CamelCaseToUnderscoreFilter.INSTANCE);
System.out.println("fastjson 2.0.32 toJSONString result: " + jsonString_2_0_32);
}
}