首页 > 其他分享 >Fastjson反序列化(一)

Fastjson反序列化(一)

时间:2022-10-23 18:58:40浏览次数:66  
标签:Fastjson name age id JSON User 序列化 public

前置知识

Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。
Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象(通过远程调用的方式)

//序列化
String text = JSON.toJSONString(obj); 
//反序列化
VO vo = JSON.parse(); //解析为JSONObject类型或者JSONArray类型
VO vo = JSON.parseObject("{...}"); //JSON文本解析成JSONObject类型
VO vo = JSON.parseObject("{...}", VO.class); //JSON文本解析成VO.class类

对象转字符串(序列化)

实体类User

package org.example;

public class User {
    public int id;
    public String name;
    public int age;

    public User() {
    }
    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

定义一个测试类

package org.example;

import org.example.User;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class Test {
    public static void main(String[] args) {
        User user = new User();
        user.setId(1);
        user.setName("zhangsan");
        user.setAge(18);

        //对象转字符串
        String s1 = JSON.toJSONString(user);
        String s2 = JSON.toJSONString(user, SerializerFeature.WriteClassName);
        System.out.println(s1);
        System.out.println(s2);
    }
}

输出结果

{"age":18,"id":1,"name":"zhangsan"}
{"@type":"org.example.User","age":18,"id":1,"name":"zhangsan"}

字符串转对象(反序列化)

package org.example;

import org.example.User;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class fastjsonTest {
    public static void main(String[] args) {
        User user = new User();
        user.setId(1);
        user.setName("zhangsan");
        user.setAge(18);

        //对象转字符串
        String s1 = JSON.toJSONString(user);
        String s2 = JSON.toJSONString(user, SerializerFeature.WriteClassName);
        System.out.println(s1);
        System.out.println(s2);

        System.out.println("s1==========================s1");
        //字符串转对象
        Object o1 = JSON.parse(s1);
        JSONObject jo1 = JSON.parseObject(s1);
        User user1 = JSON.parseObject(s1, User.class);
        System.out.println(o1);
        System.out.println(jo1);
        System.out.println(user1);

        System.out.println("s2=========================s2");
        Object o2 = JSON.parse(s2);
        JSONObject jo2 = JSON.parseObject(s2);
        User user2 = JSON.parseObject(s2, User.class);
        System.out.println(o2);
        System.out.println(jo2);
        System.out.println(user2);
    }
}

输出结果

{"age":18,"id":1,"name":"zhangsan"}
{"@type":"org.example.User","age":18,"id":1,"name":"zhangsan"}
s1==========================s1
{"name":"zhangsan","id":1,"age":18}
{"name":"zhangsan","id":1,"age":18}
User{id=1, name='zhangsan', age=18}
s2=========================s2
User{id=1, name='zhangsan', age=18}
{"name":"zhangsan","id":1,"age":18}
User{id=1, name='zhangsan', age=18}

fastjson通过JSON.toJSONString()将对象转为字符串(序列化),当使用SerializerFeature.WriteClassName参数时会将对象的类名写入@type字段中,在重新转回对象时会根据@type来指定类,进而调用该类的setget方法。因为这个特性,我们可以指定@type为任意存在问题的类,造成一些问题

利用

漏洞是利用fastjson autotype在处理json对象的时候,未对@type字段进行完全的安全性验证,攻击者可以传入危险类,并调用危险类连接远程rmi主机,通过其中的恶意类执行代码。攻击者通过这种方式可以实现远程代码执行漏洞的利用,获取服务器的敏感信息泄露,甚至可以利用此漏洞进一步对服务器数据进行修改,增加,删除等操作,对服务器造成巨大的影响。

set,get,is自动调用问题

构造Evil类

package org.example;

public class Evil {
    private String cmd;
    public Evil(){
        System.out.println("Evil()"+ this.hashCode());
    }

    public String getCmd() {
        System.out.println("getCmd()"+this.hashCode());
        return cmd;
    }

    public void setCmd(String cmd) {
        System.out.println("setCmd"+this.hashCode());
        this.cmd = cmd;
    }
}

写一个test测试下

package org.example;

import com.alibaba.fastjson.JSON;

public class Test {
    public static void main(String[] args) {
        String evil = "{\"@type\":\"org.example.Evil\",\"cmd\":\"calc\"}";
        JSON.parse(evil);
        System.out.println("-------------------");
        JSON.parseObject(evil);
        System.out.println("------------------");
        JSON.parseObject(evil, Object.class);
    }
}

跟进parseObject(evil)发现其方法发现也是使用parse解析的,但是多了一个(JSONObject)toJSON(obj)

这个方法调用的get,堆栈如下

getCmd:11, Evil (org.example)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
get:451, FieldInfo (com.alibaba.fastjson.util)
getPropertyValue:105, FieldSerializer (com.alibaba.fastjson.serializer)
getFieldValuesMap:439, JavaBeanSerializer (com.alibaba.fastjson.serializer)
toJSON:902, JSON (com.alibaba.fastjson)
toJSON:824, JSON (com.alibaba.fastjson)
parseObject:206, JSON (com.alibaba.fastjson)
main:13, Test (org.example)

那么setter在哪调用的?

com.alibaba.fastjson.util.JavaBeanInfo#build

在通过@type拿到类之后,通过反射拿到该类所有的方法存入methods,接下来遍历methods进而获取get、set方法,如上图。总结set方法自动调用的条件为:

  1. 方法名长度大于4
  2. 非静态方法
  3. 返回值为void或当前类
  4. 方法名以set开头
  5. 参数个数为1

当满足条件之后会从方法名截取属性名,截取时会判断_,如果是set_name会截取为name属性,具体逻辑如下:

当截取完但是找不到这个属性

会判断传入的第一个参数类型是否为布尔型,是的话就在截取完的变量前加上is,截取propertyName的第一个字符转大写和第二个字符,并且然后重新尝试获取属性字段。

比如:public boolean setBoy(boolean t) 会寻找isBoy字段。

set的整个判断就是:如果有setCmd()会绑定cmd属性,如果该类没有cmd属性会绑定isCmd属性。

get的判断

总结下就是:

  1. 方法名长度大于等于4
  2. 非静态方法
  3. 以get开头且第4个字母为大写
  4. 无传入参数
  5. 返回值类型继承自Collection Map AtomicBoolean AtomicInteger AtomicLong

当程序绑定了对应的字段之后,如果传入json字符串的键值中存在这个值,就会去调用执行对应的setter、构造方法

小结:

  1. parse(jsonStr) 构造方法+Json字符串指定属性的setter()+特殊的getter()
  2. parseObject(jsonStr) 构造方法+Json字符串指定属性的setter()+所有getter() 包括不存在属性和私有属性的getter()
  3. parseObject(jsonStr,Object.class) 构造方法+Json字符串指定属性的setter()+特殊的getter()

标签:Fastjson,name,age,id,JSON,User,序列化,public
From: https://www.cnblogs.com/gk0d/p/16819120.html

相关文章

  • 反序列化
    1.前言Java序列化及反序列化处理在基于Java架构的Web应用中具有尤为重要的作用。例如位于网络两端、彼此不共享内存信息的两个Web应用在进行远程通信时,无论相互间发送何种......
  • 江苏工匠杯unseping(反序列化+Linux命令执行{$(printf '\154\163')})
    <?phphighlight_file(__FILE__);classease{private$method;private$args;function__construct($method,$args){$this->method=$m......
  • 序列化器---netty
    packagecn.itcast.protocol;importcom.google.gson.*;importjava.io.*;importjava.lang.reflect.Type;importjava.nio.charset.StandardCharsets;/***用......
  • .NET Core 3.0使用JsonSerializer(System.Text.Json)序列化和反序列化JSON
    本文主要介绍.NETCore3.0中,使用JsonSerializer(System.Text.Json)对JSON数据进行序列化和反序列化的方法及示例代码。 1、使用的命名空间usingSystem.Text.Json;......
  • 直播平台开发,序列化时实现任意类型自定义转换
    直播平台开发,序列化时实现任意类型自定义转换1、前言在对象进行序列化时,希望对序列化的字段进行格式化处理,比如:Double与String转换、BigDecimal与String转换、Long与Date......
  • go json.Marshal序列化/解析失败的一种情况
    读取mysql返回struct体数据printf时可以打印通过json解析一直为空经过多方测试发现是struct内部变量命名时被写成了小写开头猜测:go定义内部变量/函数时,小写开头为私有,大......
  • drf-序列化和反序列化
    https://www.bilibili.com/video/BV1z5411D7BQ?p=8&vd_source=caabcbd2a759a67e2a3de8acbaaf08ea models.pyfromdjango.dbimportmodels#Createyourmodelsher......
  • test34-文件输入输出序列化和反序列化 msgpack map用法
    #!/usr/bin/envpython#-*-encoding:utf-8-*-'''importcsvheaders=['学号','姓名','分数']rows=[('202001','张三','98'),('202002','李四','95'),('202003&......
  • gson序列化null
    问题:当一个字段为null时,json数据不显示字段名称Mapm=NewHashMap();m.put(“a”,null);输出:newGson().toJson(m);预期结果:{a:null}实际结果:{}解决方式:使用GsonBui......
  • 源码分析之序列化器的many关键字
    在序列多个数据时,我们需要指定一个关键字many=True这是为什么呢?其实是,当序列化器产生对象时,传入参数many和不传入会生成两个不同的对象!!这是怎么实现的呢??1.类的对象生......