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);
请注意,接收器需要预先知道类的类型,且必须指定类型 MyClass.class
,作为 readValue ()
的第二个参数。
185.5.2. 什么是多态对象映射?
在某些情况下,序列化对象的接收器无法预先知道对象类型。例如,这适用于 polymorphic 对象数组的情况。考虑抽象类型、Shape
及其子类型、三角
le、Square
(等等):
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 对象映射是一种机制,它可以通过在序列化数组中提供额外元数据来标识阵列中的对象类型,可以序列化和反序列化抽象类。
多态对象映射存在固有的安全风险,因为机制允许 发送者 选择要实例化哪些类,后者可以形成发件人攻击的基础。红帽发布 FasterXML Jackson 库具有白名单机制,其提供了额外的保护级别,可防止此威胁。您必须确保使用红帽的 jackson-databind 库(由 Fuse 版本 7.7 及之后的版本提供)的发布,才能获得这个额外的保护层。如需了解更多详细信息,请参阅 第 185.5.5 节 “来自 polymorphic deserialization 的安全风险”。
要使接收器可以反序列化阵列中的对象,需要在序列化数据中提供类型元数据。默认情况下,Jackson 不会对序列化对象的任何类型元数据进行编码,因此您需要编写一些额外的代码才能启用此功能。
要启用 polymorphic 对象映射,请执行以下步骤(使用 ListOfShape
作为示例):
对于可以是列表元素的每个类(
Shape
的子类),为类标上@JsonTypeInfo
,如下所示:@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY) public class Triangle extends Shape { ... }
当
Triangle
类被序列化为 JSON 格式时,它的格式如下:{"@class":"com.example.Triangle", "property1":"value1", "property2":"value2", ...}
接收器必须配置为允许对
Triangle
、Square
及其他类进行反序列化处理,方法是将这些类添加到反序列化白名单中。要配置白名单,请将jackson.deserialization.whitelist.packages
系统属性设置为以逗号分隔的类和软件包列表。例如,要允许Triangle
、Square
类的反序列化,请设置系统属性,如下所示:-Djackson.deserialization.whitelist.packages=com.example.Triangle,com.example.Square
或者,您可以设置系统属性以允许整个
com.example
软件包:-Djackson.deserialization.whitelist.packages=com.example
注意这个白名单机制仅适用于红帽对 jackson-databind 库的发布。标准 jackson-databind 库使用黑名单机制,每次发现潜在的小工具类时,都需要更新它。
185.5.4. polymorphic deserialization 的默认映射
如果一个给定的 Java 类 com.example.MyClass
没有被白名单,仍然可以序列化类的实例,但在接收端,实例将使用通用的默认映射进行反序列化。
当在 Jackson 中启用 polymorphic 对象映射时,还有一些替代的编码方式:
With
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY)
:{"@class":"com.example.MyClass", "property1":"value1", "property2":"value2", ...}
在这种情况下,实例将使用属性反序列化到
对象
。With
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_ARRAY)
:["com.example.MyClass", {"property1":"value1", "property2":"value2", ...}]
在这种情况下,实例将反序列化到包含两个字段的 JSON 数组:
-
值为
com.example.MyClass
的字符串 -
具有两个
(或更多)属性的对象
-
With
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_OBJECT)
:{"com.example.MyClass":{"property1":"value1", "property2":"value2", ...}}
在这种情况下,实例将反序列化为一个单一字段
com.example.MyClass
,并且值作为Object
具有两个(或更多)属性。
185.5.5. 来自 polymorphic deserialization 的安全风险
使用 FasterXML jackson-databind
库的应用程序通过取消序列化 JSON 内容来实例化 Java 对象,可能会受到 远程代码执行攻击的影响。但是,这个漏洞不是自动的,如果您进行适当的缓解方案步骤,可以避免这样做。
在攻击可能前,必须至少满足以下先决条件:
您已启用了 polymorphic 类型处理,以便在
jackson-databind
中对 JSON 内容进行反序列化处理。在 Jackson JSON 中,有两种启用 polymorphic 类型处理的方法:-
使用
@JsonTypeInfo
和@JsonSubTypes
注释的组合。 -
通过调用
ObjectMapper.enableDefaultTyping ()
方法。这个选项特别危险,因为它可以有效地全局启用多态输入。
-
使用
- Java 类路径中 有一个或多个 gadget 类。gadget 类被定义为执行敏感(可能被利用)操作的任何类,作为执行构造器或 setter 方法(这是可在反序列化过程中调用的方法)的副作用。
-
您的 Java 类路径中的一个或者多个 gadget 类还没有被当前版本的
jackson-databind
列入黑名单。如果您使用 jackson-databind 库的标准发行版,则 Jackson JSON 库维护的 gadget 黑名单是针对远程代码执行漏洞的 defence 最后一行。 -
(仅限 Jackson-databind 库由红帽发布) 您明确将一个 gadget 类添加到接收器上的 deserialization whitelist (通过设置
jackson.deserialization.whitelist.packages
系统属性)。由于这不太可能这样做,白名单机制默认对所有 gadget 类提供有效的保护。