自从3.15温少公布了这个安全升级公告,在不少同行朋友圈里广为流传,最诧异的不外乎“一个序列化框架竟然会出现远程执行的漏洞”,其实如果用过fastjson的序列化类名功能就不难想到,在序列化写入类名后,反序列化时必然会用到这个类名进行对象类型的识别和创建。
public static void main(String[] args) throws IOException { Model t = new Model(); t.setName("test"); String json = JSON.toJSONString(t,SerializerFeature.WriteClassName); System.out.println(json); System.out.println(JSON.parSEObject(json,Object.class)); } static class Model{ private String name; public String getName() { return name; } public Model setName(String name) { this.name = name; return this; } }
输出:
{"@type":"fastjson.FastjsonBugTest$Model","name":"test"}
fastjson.FastjsonBugTest$Model@763d9750
在没有限制反序列化类型的前提下,理论上是可以构建出一些有意思的特殊对象的,感兴趣可以尝试。这里只说修复。
升级fastjson版本
由于线上环境比较复杂,一个业务线包含10+项目,都使用maven管理,有一些项目是maven多模块项目。加上开发时间不同,开发人员不同,没有统一管理,这就形成了错综复杂的依赖关系。单说fastjson的依赖,版本有1.2.6,1.1.46,1.1.41,1.2.8...总之是不少。
关于如何升级修复版本,官方给出了比较详尽的兼容列表,照着升就行了。
增加autotype白名单
由于在缓存层依赖fastjson使用className做反序列化的自动类型判断,直接升级的话,会禁止autotype功能,需要添加可反序列化的白名单。
static{ ParserConfig.getGlobalInstance().addAccept("com.xxxx."); }
修改完成后运行了几天是没有问题的。但是过了几天,另一个业务线的同事上线一个新功能,重启之后失败,不停地报不能实例化实例的异常。查看了异常堆栈,发现是静态代码块抛出了异常NoSuchMethodError。原因是此业务线之前的同事阴差阳错地引用了我们业务线的依赖,在使用时加载了自己的低版本的fastjson,ParserConfig不支持addAccept方法。
虽然这种方式本来就不是正确的,但是本着程序健壮性的原则,修改了一下代码,使用反射调用方法:
static { // 针对fastjson暴露的严重bug修改 开启autotytype白名单 https://github.com/alibaba/fastjson/wiki/enable_autotype String methodName = "addAccept"; String args = "com.xxxxx."; ParserConfig parserConfig = ParserConfig.getGlobalInstance(); boolean executed = false; try { for(Method m : ParserConfig.class.getDeclaredMethods()){ if(m.getName().equals(methodName)){ m.invoke(parserConfig,args); executed = true; } } } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (Throwable e){ e.printStackTrace(); } // 执行不成功的依然使用老版本fastjson,无需开启白名单 System.out.println("fastjson 漏洞修复,白名单添加结果: "+executed); }
注意要使用会优先加载的类,推荐写一个类放到容器中进行加载。 升级整体上还是比较简单的,官方给出的方法也比较详尽,只要注意一下多项目的兼容就OK了。