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
、子类型、Triangle
、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 对象映射是一种机制,可通过在序列化数组中提供额外的元数据来识别阵列中对象的类型,从而序列化和反序列化数组。
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 库使用黑名单机制,每次发现潜在的新 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 对象的应用程序可能会受到 远程代码执行攻击的影响。此漏洞不是自动的,但如果您采取适当的缓解方案步骤,可以避免此漏洞。
在攻击成为可能前,至少需要满足以下先决条件:
您已启用了 polymorphic 类型处理,以便在
jackson-databind
中反序列化 JSON 内容。在 Jackson JSON 中启用多语言类型处理有两种替代方法:-
结合使用
@JsonTypeInfo
和@JsonSubTypes
注释。 -
通过调用
ObjectMapper.enableDefaultTyping ()
方法。这个选项特别危险,因为它可以在全局范围内启用多形键入。
-
结合使用
- Java 类路径中 有一个或多个 gadget 类。gadget 类被定义为执行敏感(可能被利用)操作的任何类,作为执行构造器或集合方法(这是在反序列化期间可调用的方法)的副作用。
-
Java 类路径中的一个或多个 gadget 类尚未由当前版本的
jackson-databind
列入黑名单。如果您使用 jackson-databind 库的标准发行版,则 Jackson JSON 库维护的 gadget 黑名单是针对远程代码执行漏洞的最后一行。 -
(红帽仅发布 jackson-databind 库) 您可以将其中一个 gadget 类明确添加到接收方的反序列化白名单中(通过设置
jackson.deserialization.whitelist.packages
系统属性)。因为您不太可能这样做,因此白名单机制默认对所有 gadget 类提供有效的保护。