[JAVA安全]JACKSON反序列化

news2024/11/14 14:49:30

前言

ackson是一个开源的Java序列化和反序列化工具,可以将Java对象序列化为XML或JSON格式的字符串,以及将XML或JSON格式的字符串反序列化为Java对象。

依赖

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.7.9</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.7.9</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.7.9</version>
</dependency>

JACKSON序列化与反序列化

Jackson提供了ObjectMapper.writeValueAsString()和ObjectMapper.readValue()两个方法来实现序列化和反序列化的功能,不难看出 write就是序列化,而read 就是反序列化。

demo

package JACKSON;


public class User {
    private String username;
    private String password;
    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

测试:

package JACKSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

public class JacksonTest {
    public static void main(String[] args) throws IOException {
        User user = new User("snowy","123456");
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(user);
        System.out.println(json);
        User other = mapper.readValue(json,User.class);
        System.out.println(other);
    }
}

 运行结果

感觉和fastjson 差不多,需要解决多态的问题,在fastjson中引入的是@type解决该问题

Jackson中也有与之对应的方法,Jackson实现了JacksonPolymorphicDeserialization机制来解决这个问题,有两种方法:DefaultTyping和@JsonTypeInfo注解。

DefaultTyping

Jackson提供一个enableDefaultTyping设置,其包含4个值,查看com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping可看到相关介绍信息:

 默认情况下 DefaultTyping是第二个设置,也就是    OBJECT_AND_NON_CONCRETE   

JAVA_LANG_OBJECT

JAVA_LANG_OBJECT:当被序列化或反序列化的类里的属性被声明为一个Object类型时,会对该Object类型的属性进行序列化和反序列化,并且明确规定类名。(当然,这个Object本身也得是一个可被序列化的类)

demo  - User2

public class User2 {
    public String name="snowy";
}

在User类里添加一个Object属性,并实现getter、setter、toString等方法

test

public class JacksonTest {
    public static void main(String[] args) throws IOException {
        User user = new User("snowy","777",new user2());
        ObjectMapper mapper = new ObjectMapper();

        //JAVA_LANG_OBJECT
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT);

        String json = mapper.writeValueAsString(user);
        System.out.println(json);
        User other = mapper.readValue(json,User.class);
        System.out.println(other);
    }
}

运行结果

通过对比可以看出,设置JAVA_LANG_OBJECT后,会对Object属性对象进行了序列化和反序列化操作,会将对应的类名一并输出。 

OBJECT_AND_NON_CONCRETE

默认操作,当类里有Interface、AbstractClass类时,对其进行序列化和反序列化。

NON_CONCRETE_AND_ARRAYS

支持Array类型的反序列化和序列化

NON_FINAL

支持除声明为final之外的类型。

总结

@JsonTypeinfo 注解

@JsonTypeinfo 注解是 JACKSON多态类型绑定的一种方式,支持下面5种类型的取值:

@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM)

JSonTypeinfo.Id.None:

默认值,设不设置都一样。

JsonTypeInfo.Id.CLASS:

在User 添加 jsontypeinfo注解

 运行结果:

设置前:
{"username":"snowy","password":"777","object":["JACKSON.user2",{"name":"snowy2","passwd":"user2"}]}
JACKSON.User@7d0587f1

设置后:
{"username":"snowy","password":"777","object":{"@class":"JACKSON.user2","name":"snowy2","passwd":"user2"}}
JACKSON.User@2833cc44

输出看到 object 属性中多了 "@class":"JACKSON.user2",即含有具体的类的信息。同时反序列化出来的object 属性  user2类对象,即能够成功对指定类型进行序列化和反序列化。也就是说,在Jackson 反序列化的时候如果使用了 JsonTypeInfo.Id.CLASS 修饰的话,可以通过@class  的方式指定相关类,并进行相关调用

JsonTypeInfo.Id.MINIMAL_CLASS

修改 User 类中的 object 属性 @jsonTypeinfo 注解值为 JsonTypeinfo.ld.MINIMAL_CLASS.

@JsonTypeInfo( use = JsonTypeInfo.Id.MINIMAL_CLASS)

运行结果:

{"username":"snowy","password":"777","object":{"@c":"JACKSON.user2","name":"snowy2","passwd":"user2"}}
JACKSON.User@27a8c74e

输出看到 object 属性多了  "@c":"JACKSON.user2"  ,即使用 @c 代替了 @class ,官方描述中的意思是缩短了相关类名,实际上 效果和 Jsontypeinfo.ld.class是类似的。能够成功对指定类型进行序列化和反序列化都可以用于指定相关类并进行调用

JsonTypeInfo.Id.NAME

修改User类中的 @JsonTypeInfo 注解为 JsonTypeInfo.Id.NAME

@JsonTypeInfo( use = JsonTypeInfo.Id.NAME)

输出结果

{"username":"snowy","password":"777","object":{"@type":"user2","name":"snowy2","passwd":"user2"}}

输出能看到  object 属性多了 "@type":"user2",但是没有具体的包名再内的类名。因此再后面反序列化的时候会发生报错,也就是说,这个设置是不能被反序列化利用 的 ,如图所示

JsonTypeinfo.id.CUSTOM

这个值是提供给用户自定义的意思,我们是无法直接使用的,需要手动写一哥解析器才能配合使用,直接运行会抛出异常

Exception in thread "main" java.lang.IllegalStateException: Do not know how to construct standard type id resolver for idType: CUSTOM

小结:

由前面得知,当@JsonTypeInfo 注解设置为如下 值之一 并且修饰的是Object属性时,可以利用来触发Jackson 反序列化漏洞

  • JsonTypeInfo.Id.CLASS (@class)
  • JsonTypeInfo.Id.MINIMAL_CLASS (@c)

调试分析

Jackson反序列化的过程 其实就分为两步,第一步是通过构造函数生成实例,第二部是设置实例的属性值。

我们以第一个例子来进行Jackson 反序列化过程的调试分析,在

User other = mapper.readValue(json,User.class);

处打上断点进行调试,同时在getter   stter 上打断点,开始调试,顺便在 User中添加一个 构造函数

public User() {
        System.out.println("构造函数触发了");
    }

跟进readValue

跟进   _readMapAndClose  ,一步一步跳,经过两个不匹配的if,将会跳到此处的  deserialize  

 进入 deserialize 

  因为这里的  this._vanillaProcessing 为true  所以他会调用下方的 this._vanillaProcessing   

 跟进  vanillaDeserialize()   ,跟进去,BeanDeserializer.vanillaDeserialize() 函数的实现比较简单,先调用createUsingDefault() 函数来调用指定类的无参构造函数来生成类实例:

 进入call ,实例化返回

 

此时 实例化完之后,User的构造器 已经触发了:

 上面那个序列化内容不用看,因为是我忘记注释 

String json = mapper.writeValueAsString(user);

BeanDeserializer.vanillaDeserialize() 函数调用完 无参的类构造函数生成 实例Bean(也就是User)后,就开始进入do  while 循环来循环解析键值对中的属性值并调用 deserializeAndSet() 函数来解析并设置Bean 的属性值

 跟进去  SettableBeanProperty.deserialize()函数,可以看到有两个反序列化的代码逻辑,其中if判断语句会判断当前反序列化的内容是否携带类型,若是则调用deserializeWithType()函数解析,否则直接调用deserialize()函数解析:

我们的第一个类型是username  也就是string  就会跳到下面的调用,相反如果你的值是int 则会进入第一个if。我们进入的是  StringDeserializer.deserialize()   

跟进deserialize()后,用了p.getText(),获取到了p的属性值

一路跳过,最终返回到前面,通过invoke 命令 调用到了 username的setter方法

password属性也是String类型的,所以通过前边的do while循环,在调用deserializeAndSet();获取对应的值,跟username过程完全一样不看了 。

至此,整个函数调用过程大致过了一遍。使用@JsonTypeInfo注解的函数调用过程也是一样的。

简单梳理一遍,Jackson反序列化的过程为,先调用通过无参的构造函数生成目标类实例,接着是根据属性值是否是数组的形式即是否带类名来分别调用不同的函数来设置实例的属性值,其中会调用Object类型属性的构造函数和setter方法。

总结:

在Jackson反序列化中,若调用了enableDefaultTyping()函数或使用@JsonTypeInfo注解指定反序列化得到的类的属性为JsonTypeInfo.Id.CLASS或JsonTypeInfo.Id.MINIMAL_CLASS,则会调用该属性的类的构造函数和setter方法。

反序列化漏洞

前提条件

满足下面三点之一 即存在Jackson 反序列化漏洞:

  • 调用了 ObjectMapper.enableDefaultTyping()函数
  • 对要进行反序列化的类的属性使用了值为JsonTypeInfo.Id.CLASS的@JsonTypeInfo注解;
  • 对要进行反序列化的类的属性使用了值为JsonTypeInfo.Id.MINIMAL_CLASS的@JsonTypeInfo注解;

漏洞原理

由之前的结论知道,当使用的JacksonPolymorphicDeserialization机制配置有问题时,Jackson反序列化就会调用属性所属类的构造函数和setter方法。而如果该构造函数或setter方法存在危险操作,那么就存在Jackson反序列化漏洞

DEMO

package JACKSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

public class JacksonTest {
    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping();

        String json = "{\"age\":17,\"name\":\"snowy\",\"sex\":[\"JACKSON.MySex\",{\"sex\":1}]}";
        Person p2 = mapper.readValue(json, Person.class);
        System.out.println(p2);
    }
}
package JACKSON;

public class MySex implements Sex {
    int sex;
    public MySex() {
        System.out.println("MySex构造函数");
    }

    @Override
    public int getSex() {
        System.out.println("MySex.getSex");
        return sex;
    }

    @Override
    public void setSex(int sex) {
        System.out.println("MySex.setSex");
        this.sex = sex;
        try {
            Runtime.getRuntime().exec("calc");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
package JACKSON;

public class Person {
    public int age;
    public String name;
    //    @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)
    public Sex sex;

    public Person() {
        System.out.println("Person构造函数");
    }

    @Override
    public String toString() {
        return String.format("Person.age=%d, Person.name=%s, %s", age, name, sex == null ? "null" : sex);
    }
}
package JACKSON;

public interface Sex {
    public void setSex(int sex);
    public int getSex();
}

参考文章

Jackson系列一——反序列化漏洞基本原理 [ Mi1k7ea ]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/182449.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中国省际铁路通行时间数据

一、数据简介 本数据来自南京大学长江产业经济研究院《全国统一大市场下的省际铁路交通研究报告》的附录部分。中国的铁路&#xff08;高铁&#xff09;建设取得了辉煌成果。但受铁路时刻众多、历史数据不容易搜集整理的限制&#xff0c;学术与政策研究者一直无法对铁路建设的时…

三、JDBC详解

教程相关资料&#xff1a;https://www.aliyundrive.com/s/wMiqbd4Zws6 1&#xff0c;JDBC概述 在开发中我们使用的是java语言&#xff0c;那么势必要通过java语言操作数据库中的数据。这就是接下来要学习的JDBC。 1.1 JDBC概念 JDBC 就是使用Java语言操作关系型数据库的一套…

Nacos 配置中心 服务端推送变更源码讲解

目录 1. 配置引起变更的两种方式 1.1 后台管理直接操作 1.2 NacosClient 调用 RPC 接口 2. 变更事件处理 AsyncNotifyService 2.1 HTTP 任务 2.2 RPC任务 2.3 NacosServer 其他节点接收到消息后如何处理 3. 客户端推送实现&#xff1a;DumpService.dump 接着上一篇 Nac…

1601_读Dennis M. Ritchie and Ken Thompson的The UNIX TimeSharing System_Unix分时复用系统

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 很久之前就闻听了UNIX的大名&#xff0c;也看过很多相关的故事类文章。其中最让我印象深刻的莫过于Ken发明UNIX的故事以及这个系统对于Linux以及GNU的OS的影响&…

Linux操作系统之线程同步

文章目录一、线程的实现&#xff1a;用户级、内核级、组合二、线程的同步&#xff08;通讯&#xff09;&#xff1a;信号量&#xff0c;互斥锁&#xff0c;条件变量&#xff0c;读写锁1&#xff09;信号量2&#xff09;互斥锁操作&#xff1a;加锁&#xff0c;解锁。3&#xff…

浮躁的当下或许我们更加需要一颗平常心

科技的迅速发展&#xff0c;已经融入我们的生活&#xff1b;曾经的慢生活慢节奏早已离我们远去&#xff0c;取而代之的是伴随科技齿轮快速运转的生活&#xff0c;快节奏更是科技时代下人们普遍生活的真实写照。 然而&#xff0c;科技虽然带给了我们生活的极大的便利&#xff0…

php伪协议

目录 一、伪协议介绍 1、php://协议 2、php://filter伪协议 3.php://input&#xff08;读取POST数据&#xff09; 4、file伪协议 5、phar://伪协议(读取压缩包文件内容) 6、压缩文件伪协议 6.1.zip://[压缩文件绝对路径]%23压缩文件内的子文件名 6.2.compress.bzip2://…

Mybatis 的输出结果封装小细节

resultType 属性可以指定结果集的类型&#xff0c;它支持基本类型和实体类类型。我们在前面的 CRUD 案例中已经对此属性进行过应用了。需要注意的是&#xff0c;它和 parameterType 一样&#xff0c;如果注册过类型别名的&#xff0c;可以直接使用别名。没有注册过的必须使用全…

Markdown基础语法

文章目录Markdown基础语法一、标题使用&#xff1a;2、二级标题3、一次类推二、字体三、引用四、分割线五、插入图片六、超链接七、列表1、有序列表2、无序列表八、表格九、代码十、添加目录九、代码十、添加目录Markdown基础语法 一、标题使用&#xff1a; 格式&#xff1a;…

智能驾驶 车牌检测和识别(二)《YOLOv5实现车牌检测(含车牌检测数据集和训练代码)》

智能驾驶 车牌检测和识别&#xff08;二&#xff09;《YOLOv5实现车牌检测&#xff08;含车牌检测数据集和训练代码&#xff09;》 目录 智能驾驶 车牌检测和识别&#xff08;二&#xff09;《YOLOv5实现车牌检测&#xff08;含车牌检测数据集和训练代码&#xff09;》 1. 前…

[Vulnhub] DC-4

下载链接&#xff1a;https://download.vulnhub.com/dc/DC-4.zip 同DC-3 这个靶机也是只有一个flag。 全面信息搜集hydra爆破登录和ssh密码teehee命令(写入文件内容)提权/etc/passwd & /etc/sudoers 文件利用 目录 <1> 信息搜集 <2> hydra爆破登录密码 <…

怎么解除pdf的加密,建议收藏这几种方法

我们在下载资料的时候经常能遇到PDF文档&#xff0c;有时候这些文档还会被设置需要输入密码才能打开。有些朋友找不到密码就只能放弃这份资料&#xff0c;其实还可以再争取一下&#xff0c;解除PDF的加密不是没有可能的。将PDF的密码破除后我们就不再需要密码就能阅读使用该文档…

SpringBoot利用AOP线程池异步设置缓存

文章目录设置缓存1、定义注解2、AOP3、测试设置缓存 1、定义注解 注解定义四个属性&#xff0c;分别是&#xff1a; value&#xff0c;key的别名key : redis的key,如果key不设置&#xff0c;则会用方法名加参数列表作为keyexpire&#xff1a;失效时间&#xff0c;默认为 1天…

Dockerfile创建镜像并上传到Docker Hub

Dockerfile创建镜像并上传到Docker Hub1. Dockerfile1.1 准备文件1.2 构建镜像2. 上传docker hub参考1. Dockerfile 通过Dockerfile构建镜像 1.1 准备文件 在某个空文件夹&#xff08;假设名为test&#xff09;下编写Dockerfile文件 # 声明使用哪个基础镜像 FROM ubuntu:20.0…

Spring Cloud微服务网关Gateway组件学习笔记

目录 网关简介 1. 什么是Spring Cloud Gateway 1.1 核心概念 1.2 工作原理 2. Spring Cloud Gateway快速开始 2.1 环境搭建 2.2集成Nacos 3. 路由断言工厂&#xff08;Route Predicate Factories&#xff09;配置 3.1内置断言工厂 3.2自定义 4. 过滤器工厂&#xf…

Android Studio实现志愿者系统

项目目录一、项目概述二、主要技术三、开发环境四、详细设计1、基础Activity2、活动信息3、成员信息4、百度地图5、Widget组件五、运行演示一、项目概述 本系统采用MVC架构设计&#xff0c;SQLite数据表有用户表、成员表和活动表&#xff0c;有十多个Activity页面。打开应用&a…

123、【回溯算法】leetcode ——491. 递增子序列:unordered_set去重和int数组去重(C++版本)

题目描述 原题描述&#xff1a;491. 递增子序列 解题思路 此题也是子集问题&#xff0c;但和 90.子集II &#xff08;子集问题startIndex去重&#xff09; 的区别在于&#xff1a;&#xff08;1&#xff09;存储结果集判定条件&#xff1b;&#xff08;2&#xff09;输入数据…

Python学习笔记十二之十大经典排序算法

Python学习笔记十二之十大经典排序算法 排序算法是《数据结构与算法》中最基本的算法之一。排序算法可以分为内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#xff0c;而外部排序是因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;…

zos JESMSGLG 不显示 job steps return codes

zos JESMSGLG 不显示 job steps return codes 在普通学习的时候&#xff0c;SDSF 查看 JCL 结果&#xff0c;可能并不会注意到下面结果有什么差别 但跟公司的大型机出的结果是不同的&#xff0c;就是缺少了 job steps return codes&#xff0c;在 STARTED 和 ENDED 中间应该有…

【Linux】冯诺依曼体系结构和操作系统

目录 一、冯诺依曼体系结构 1.组成 2.各结构特性 二、操作系统 1.概念 2.设计OS的目的 3.如何理解 "管理" 4.系统调用 一、冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依…