这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战
记录一次fastjson产生的bug
由于fastjson采白名单的方法来防御反序列化满洞,导致当黑客不断发屈新的反序列化Gadgets类时,在autoType关闭的情况下仍然可能可以绕过黑白名单防御机制,造成远程命令执行漏洞。经研究,该漏洞利用门槛较低,可绕过autoType限制,风险影响较大。阿里云应急响应中心提醒fastjson用户尽快采取安全措施阻止漏洞攻击。
但是在升级的过程中,有一个项目出现了问题。出现的问题是,使用大写字母作为属性命名的javaBean,在使用这个方法时出问题。
//parseObject(String text, Class<T> clazz)
Input input = JSONObject.parseObject(request.getData().toJSONString(), Input.class);
上面的代码中,得出来的input为空。
在fastjson的DefaultJSONParser这个类中两个不同版本的fastjson处理选择了不同的方式。
1.2.12
public <T> T parseObject(Type type, Object fieldName) {
int token = this.lexer.token();
if (token == 8) {
this.lexer.nextToken();
return null;
} else {
if (token == 4) {
if (type == byte[].class) {
byte[] bytes = this.lexer.bytesValue();
this.lexer.nextToken();
return bytes;
}
if (type == char[].class) {
String strVal = this.lexer.stringVal();
this.lexer.nextToken();
return strVal.toCharArray();
}
}
ObjectDeserializer derializer = this.config.getDeserializer(type);
try {
//这里有所不同
return derializer.deserialze(this, type, fieldName);
} catch (JSONException var6) {
throw var6;
} catch (Throwable var7) {
throw new JSONException(var7.getMessage(), var7);
}
}
}
从调试中可以看到,1.2.12版本中属性赋值使用的是通过属性的set方法进行的。
1.2.70版本
public <T> T parseObject(Type type, Object fieldName) {
int token = this.lexer.token();
if (token == 8) {
this.lexer.nextToken();
return null;
} else {
if (token == 4) {
if (type == byte[].class) {
byte[] bytes = this.lexer.bytesValue();
this.lexer.nextToken();
return bytes;
}
if (type == char[].class) {
String strVal = this.lexer.stringVal();
this.lexer.nextToken();
return strVal.toCharArray();
}
}
ObjectDeserializer deserializer = this.config.getDeserializer(type);
try {
if (deserializer.getClass() == JavaBeanDeserializer.class) {
if (this.lexer.token() != 12 && this.lexer.token() != 14) {
throw new JSONException("syntax error,except start with { or [,but actually start with " + this.lexer.tokenName());
} else {
//更新后走了这里
return ((JavaBeanDeserializer)deserializer).deserialze(this, type, fieldName, 0);
}
} else {
//原来的版本走的是这里
return deserializer.deserialze(this, type, fieldName);
}
} catch (JSONException var6) {
throw var6;
} catch (Throwable var7) {
throw new JSONException(var7.getMessage(), var7);
}
}
}
而1.2.70却是通过这样的方式去解析的。
最后经过查阅资料,发现可以使用@JSONField(name = "AAA001")这个注解来解决这个问题,在入参的类上进行标注,大写的入参也可以转换了。
@JSONField(name = "AAA001)
private String AAA001; //属性1
同时有部分人因为使用lombok插件,导致@JSONField(name = "AAA001")标注在属性上依旧不起作用,此时可以通过显示声明属性的get方法来达成目的。
@JSONField(name = "AAA001")
public String getAAA001() {
return AAA001;
}