Jackson反序列化
字数 2384 2025-08-24 07:48:33

Jackson反序列化安全研究

1. Jackson简介

Jackson是一个开源的Java序列化和反序列化工具,可以将Java对象序列化为XML或JSON格式的字符串,以及将XML或JSON格式的字符串反序列化为Java对象。由于其使用简单、速度较快且不依赖除JDK外的其他库,被众多用户所使用。

1.1 基本依赖

<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>

2. Jackson基本使用

2.1 序列化与反序列化

Jackson提供了ObjectMapper.writeValueAsString()ObjectMapper.readValue()两个方法来实现序列化和反序列化的功能。

示例POJO类

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;
    }
    
    // getter和setter方法
    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; }
}

测试代码

public class JacksonTest {
    public static void main(String[] args) throws IOException {
        User user = new User("Sentiment", "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);
    }
}

输出结果

{"username":"Sentiment","password":"123456"}
Jackson.User@67b6d4ae

3. Jackson多态处理

Jackson实现了JacksonPolymorphicDeserialization机制来解决多态问题,有两种方法:DefaultTyping@JsonTypeInfo注解。

3.1 DefaultTyping

Jackson提供enableDefaultTyping设置,包含4个值:

  1. JAVA_LANG_OBJECT
  2. OBJECT_AND_NON_CONCRETE(默认)
  3. NON_CONCRETE_AND_ARRAYS
  4. NON_FINAL

3.1.1 JAVA_LANG_OBJECT

当被序列化或反序列化的类里的属性被声明为Object类型时,会对该Object类型的属性进行序列化和反序列化,并明确记录类名。

示例代码:

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

// 在User类中添加Object属性
private Object object;

// 测试代码
public class JacksonTest {
    public static void main(String[] args) throws IOException {
        User user = new User("Sentiment", "123456", 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):

{"username":"Sentiment","password":"123456","object":["Jackson.User2",{"name":"Tana"}]}
User{username='Sentiment', password='123456', object=Jackson.User2@1753acfe}

输出结果(不设置JAVA_LANG_OBJECT):

{"username":"Sentiment","password":"123456","object":{"name":"Tana"}}
User{username='Sentiment', password='123456', object={name=Tana}}

3.1.2 其他DefaultTyping类型

DefaultTyping类型 描述说明
JAVA_LANG_OBJECT 属性的类型为Object
OBJECT_AND_NON_CONCRETE 属性的类型为Object、Interface、AbstractClass
NON_CONCRETE_AND_ARRAYS 属性的类型为Object、Interface、AbstractClass、Array
NON_FINAL 所有除了声明为final之外的属性

3.2 @JsonTypeInfo注解

@JsonTypeInfo注解是Jackson多态类型绑定的另一种方式,支持5种类型的取值:

  1. @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
  2. @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
  3. @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)
  4. @JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
  5. @JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM)

3.2.1 JsonTypeInfo.Id.CLASS

public class User {
    private String username;
    private String password;
    
    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
    private Object object;
    // ...
}

输出结果:

{"username":"Sentiment","password":"123456","object":{"@class":"Jackson.User2","name":"Tana"}}
User{username='Sentiment', password='123456', object=Jackson.User2@5b275dab}

3.2.2 JsonTypeInfo.Id.MINIMAL_CLASS

{"username":"Sentiment","password":"123456","object":{"@c":"Jackson.User2","name":"Tana"}}
User{username='Sentiment', password='123456', object=Jackson.User2@29774679}

3.2.3 JsonTypeInfo.Id.NAME

类似于fastjson的@type字段,但会抛出异常,不能进行反序列化:

{"username":"Sentiment","password":"123456","object":{"@type":"User2","name":"Tana"}}
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Could not resolve ...

3.2.4 JsonTypeInfo.Id.CUSTOM

直接抛出异常,需要用户自定义来使用。

4. 反序列化流程分析

Jackson反序列化时会调用对应类的setter和无参构造器。对于Object类型的属性,会分别调用外层类和内层类的无参构造。

4.1 关键流程

  1. 调用vanillaDeserialize()方法
  2. 通过createUsingDefault()函数调用指定类的无参构造函数生成类实例
  3. 进入do-while循环解析键值对中的属性值
  4. 调用deserializeAndSet()函数解析并设置Bean的属性值
  5. 对于Object类型属性,会判断反序列化内容是否携带类型信息
  6. 根据类型信息实例化对应类并设置属性

5. Jackson反序列化漏洞

5.1 漏洞前提条件

满足以下任一条件即存在Jackson反序列化漏洞:

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

5.2 漏洞原理

Jackson在进行反序列化时会执行构造器和setter方法,如果这些方法中包含恶意代码,就会导致恶意代码执行。

恶意代码示例:

public class User2 {
    public String name = "Tana";
    
    public User2() {
        System.out.println("User2无参构造");
    }
    
    public void setName(String name) {
        System.out.println("User2.setName");
        this.name = name;
        try {
            Runtime.getRuntime().exec("calc");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

当反序列化包含User2实例的JSON数据时,会触发计算器程序的执行。

6. 安全建议

  1. 避免使用enableDefaultTyping()方法
  2. 避免使用@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)
  3. 对不可信的JSON输入进行严格过滤
  4. 及时更新Jackson到最新版本,修复已知漏洞
  5. 使用白名单机制限制可反序列化的类

7. 总结

Jackson反序列化漏洞的核心原理是在反序列化过程中执行了构造器和setter方法,如果这些方法中包含恶意代码就会导致安全问题。实际攻击中通常会结合特定的调用链(gadget chains)来实现更复杂的攻击。

Jackson反序列化安全研究 1. Jackson简介 Jackson是一个开源的Java序列化和反序列化工具,可以将Java对象序列化为XML或JSON格式的字符串,以及将XML或JSON格式的字符串反序列化为Java对象。由于其使用简单、速度较快且不依赖除JDK外的其他库,被众多用户所使用。 1.1 基本依赖 2. Jackson基本使用 2.1 序列化与反序列化 Jackson提供了 ObjectMapper.writeValueAsString() 和 ObjectMapper.readValue() 两个方法来实现序列化和反序列化的功能。 示例POJO类 测试代码 输出结果 3. Jackson多态处理 Jackson实现了JacksonPolymorphicDeserialization机制来解决多态问题,有两种方法: DefaultTyping 和 @JsonTypeInfo 注解。 3.1 DefaultTyping Jackson提供 enableDefaultTyping 设置,包含4个值: JAVA_LANG_OBJECT OBJECT_AND_NON_CONCRETE (默认) NON_CONCRETE_AND_ARRAYS NON_FINAL 3.1.1 JAVA_ LANG_ OBJECT 当被序列化或反序列化的类里的属性被声明为Object类型时,会对该Object类型的属性进行序列化和反序列化,并明确记录类名。 示例代码: 输出结果(设置JAVA_ LANG_ OBJECT): 输出结果(不设置JAVA_ LANG_ OBJECT): 3.1.2 其他DefaultTyping类型 | DefaultTyping类型 | 描述说明 | |------------------|----------| | JAVA_ LANG_ OBJECT | 属性的类型为Object | | OBJECT_ AND_ NON_ CONCRETE | 属性的类型为Object、Interface、AbstractClass | | NON_ CONCRETE_ AND_ ARRAYS | 属性的类型为Object、Interface、AbstractClass、Array | | NON_ FINAL | 所有除了声明为final之外的属性 | 3.2 @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) 3.2.1 JsonTypeInfo.Id.CLASS 输出结果: 3.2.2 JsonTypeInfo.Id.MINIMAL_ CLASS 3.2.3 JsonTypeInfo.Id.NAME 类似于fastjson的@type字段,但会抛出异常,不能进行反序列化: 3.2.4 JsonTypeInfo.Id.CUSTOM 直接抛出异常,需要用户自定义来使用。 4. 反序列化流程分析 Jackson反序列化时会调用对应类的setter和无参构造器。对于Object类型的属性,会分别调用外层类和内层类的无参构造。 4.1 关键流程 调用 vanillaDeserialize() 方法 通过 createUsingDefault() 函数调用指定类的无参构造函数生成类实例 进入do-while循环解析键值对中的属性值 调用 deserializeAndSet() 函数解析并设置Bean的属性值 对于Object类型属性,会判断反序列化内容是否携带类型信息 根据类型信息实例化对应类并设置属性 5. Jackson反序列化漏洞 5.1 漏洞前提条件 满足以下任一条件即存在Jackson反序列化漏洞: 调用了 ObjectMapper.enableDefaultTyping() 函数 对要进行反序列化的类的属性使用了值为 JsonTypeInfo.Id.CLASS 的 @JsonTypeInfo 注解 对要进行反序列化的类的属性使用了值为 JsonTypeInfo.Id.MINIMAL_CLASS 的 @JsonTypeInfo 注解 5.2 漏洞原理 Jackson在进行反序列化时会执行构造器和setter方法,如果这些方法中包含恶意代码,就会导致恶意代码执行。 恶意代码示例: 当反序列化包含User2实例的JSON数据时,会触发计算器程序的执行。 6. 安全建议 避免使用 enableDefaultTyping() 方法 避免使用 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 或 @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS) 对不可信的JSON输入进行严格过滤 及时更新Jackson到最新版本,修复已知漏洞 使用白名单机制限制可反序列化的类 7. 总结 Jackson反序列化漏洞的核心原理是在反序列化过程中执行了构造器和setter方法,如果这些方法中包含恶意代码就会导致安全问题。实际攻击中通常会结合特定的调用链(gadget chains)来实现更复杂的攻击。