185.5. Jackson ObjectMapper


185.5.1. 什么是对象映射?

jackson 提供了使用 com.fasterxml.jackson.databind.ObjectMapper 类序列化 Java 对象的机制。例如,您可以使用 ObjectMapper 序列化 MyClass java 对象,如下所示:

ObjectMapper objectMapper = new ObjectMapper();
MyClass myobject = new MyClass("foo", "bar");
objectMapper.writeValue(new File("myobject.json"), myobject);

对象 myobject 将被序列化为 JSON 格式并写入文件 myobject.json (Jackson 也支持转换为 XML 和 YAML 格式)。

要反序列化文件的 JSON 内容 myobject.json,您可以调用 ObjectMapper,如下所示:

ObjectMapper objectMapper = new ObjectMapper();
MyClass myobject = objectMapper.readValue(new File("myobject.json"), MyClass.class);

请注意,接收器需要提前知道类的类型,并且必须指定 type MyClass.class,作为 readValue () 的第二个参数。

185.5.2. 什么是 polymorphic 对象映射?

在某些情况下,序列化对象的接收器无法预先知道对象类型。例如,这适用于 polymorphic 对象数组的情况。考虑抽象类型、Shape、子类型、TriangleSquare (等等):

package com.example;
...
public abstract class Shape {
}

public class Triangle extends Shape {
  ...
}

public class Square extends Shape {
  ...
}

public class ListOfShape {
  public List<Shape> shapes;
  ...
}

您可以实例化并序列化一个表单列表(ListOfShape),如下所示:

ObjectMapper objectMapper = new ObjectMapper();

ListOfShape shapeList = new ListOfShape();
shapeList.shapes = new ArrayList<Shape>();
shapeList.shapes.add(new Triangle());
shapeList.shapes.add(new Square());

String serialized = objectMapper.writeValueAsString(shapeList);

但是,在接收器一侧出现问题。您可以将这个类型指定为 readValue () 的第二个参数来告知接收器预期 ListOfShape 对象:

MyClass myobject = objectMapper.readValue(serialized, ListOfShape.class);
ObjectMapper objectMapper = new ObjectMapper();

但是,接收器无法知道列表的第一个元素是 Triangle,第二个元素是 Square。要临时解决这个问题,您需要启用 polymorphic 对象映射, 如下一节所述。

185.5.3. 如何启用 polymorphic 对象映射

polymorphic 对象映射是一种机制,可通过在序列化数组中提供额外的元数据来识别阵列中对象的类型,从而序列化和反序列化数组。

重要

polymorphic 对象映射导致了固有的安全风险,因为该机制允许 发送者 选择哪个类实例化,从而形成发送方的攻击基础。红帽对 FasterXML Jackson 库的分发具有白名单机制,它提供额外的保护级别。您必须确保使用红帽的 jackson-databind 库发布(由 Fuse 版本 7.7 及更新版本提供)以获取这个额外的保护层。如需了解更多详细信息,请参阅 第 185.5.5 节 “polymorphic deserialization 的安全风险”

要使接收器可以反序列化阵列中的对象,需要在序列化数据中提供类型元数据。默认情况下,Jackson 不会对序列化对象的任何类型元数据进行编码,因此您需要编写一些额外的代码来启用此功能。

要启用 polymorphic 对象映射,请执行以下步骤(使用 ListOfShape 作为示例):

  1. 对于可以列表元素的每个类( Shape的子类),给类标上 @JsonTypeInfo,如下所示:

    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY)
    public class Triangle extends Shape {
      ...
    }
  2. Triangle 类被序列化为 JSON 格式时,它的格式如下:

    {"@class":"com.example.Triangle", "property1":"value1", "property2":"value2", ...}
  3. 接收器必须配置为允许对 TriangleSquare 和其他格式类进行反序列化,方法是将这些类添加到反序列化白名单中。要配置白名单,请将 jackson.deserialization.whitelist.packages 系统属性设置为以逗号分隔的类和软件包列表。例如,要允许对 TriangleSquare 类进行反序列化,请按如下所示设置系统属性:

    -Djackson.deserialization.whitelist.packages=com.example.Triangle,com.example.Square

    另外,您可以设置系统属性来允许整个 com.example 软件包:

    -Djackson.deserialization.whitelist.packages=com.example
    注意

    此白名单机制仅适用于红帽对 jackson-databind 库的分发。标准 jackson-databind 库使用黑名单机制,每次发现潜在的新 gadget 类时都需要更新该机制。

185.5.4. polymorphic deserialization 的默认映射

如果给定的 Java 类 com.example.MyClass 未列入白名单,则仍有可能对类序列化实例,但在接收端,将使用通用默认映射来反序列化实例。

当在 Jackson 中启用 polymorphic 对象映射时,有几个替代方法对对象进行编码:

  • 使用 @JsonTypeInfo (use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY):

    {"@class":"com.example.MyClass", "property1":"value1", "property2":"value2", ...}

    在这种情况下,实例将反序列化到带有 属性的对象

  • 使用 @JsonTypeInfo (use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_ARRAY):

    ["com.example.MyClass", {"property1":"value1", "property2":"value2", ...}]

    在这种情况下,实例将反序列化到一个 JSON 数组,其中包含两个字段:

    • 带有值 com.example.MyClass的字符串
    • 具有两个 (或更多)属性的对象
  • 使用 @JsonTypeInfo (use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_OBJECT):

    {"com.example.MyClass":{"property1":"value1", "property2":"value2", ...}}

    在这种情况下,实例将反序列化为带有单个字段 com.example.MyClass 的 JSON 映射,值是 Object with two (或更多)属性。

185.5.5. polymorphic deserialization 的安全风险

使用 FasterXML jackson-databind 库通过反序列化 JSON 内容来实例化 Java 对象的应用程序可能会受到 远程代码执行攻击的影响。此漏洞不是自动的,但如果您采取适当的缓解方案步骤,可以避免此漏洞。

在攻击成为可能前,至少需要满足以下先决条件:

  1. 您已启用了 polymorphic 类型处理,以便在 jackson-databind 中反序列化 JSON 内容。在 Jackson JSON 中启用多语言类型处理有两种替代方法:

    1. 结合使用 @JsonTypeInfo@JsonSubTypes 注释。
    2. 通过调用 ObjectMapper.enableDefaultTyping () 方法。这个选项特别危险,因为它可以在全局范围内启用多形键入。
  2. Java 类路径中 有一个或多个 gadget 类。gadget 类被定义为执行敏感(可能被利用)操作的任何类,作为执行构造器或集合方法(这是在反序列化期间可调用的方法)的副作用。
  3. Java 类路径中的一个或多个 gadget 类尚未由当前版本的 jackson-databind 列入黑名单。如果您使用 jackson-databind 库的标准发行版,则 Jackson JSON 库维护的 gadget 黑名单是针对远程代码执行漏洞的最后一行。
  4. (红帽仅发布 jackson-databind 库) 您可以将其中一个 gadget 类明确添加到接收方的反序列化白名单中(通过设置 jackson.deserialization.whitelist.packages 系统属性)。因为您不太可能这样做,因此白名单机制默认对所有 gadget 类提供有效的保护。
Red Hat logoGithubRedditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

© 2024 Red Hat, Inc.