如何在反序列化之前验证对象
How to validate an object before deserializing
在我的代码中,我从从 JMS 队列接收到的 ObjectMessage 对象调用 getObject() 方法。 Fortify 报告抱怨此 getObject() 方法的错误名称如下 Dynamic Code Evaluation: Unsafe Deserialization
。
基本上它说,我不应该在不验证对象流的内容的情况下反序列化不受信任的数据。下面是代码。
请问我应该如何以及使用什么方法来摆脱这个 Fortify 报告错误。
if (message instanceof ObjectMessage) {
ObjectMessage objMessage = (ObjectMessage) message;
Object objReportMessage = objMessage.getObject();
....
这是 Fortify 报告的问题和建议。然后它将此错误指向上面的代码 objMessage.getObject();
动态代码评估:不安全的反序列化(1 期)
摘要
在 运行 时间反序列化用户控制的对象流可以允许攻击者在
服务器,滥用应用程序逻辑或导致拒绝服务。
说明
Java 序列化将对象图转换为包含对象本身和
必要的元数据从字节流中重建它们。开发人员可以创建自定义代码来帮助
在反序列化 Java 对象的过程中,他们甚至可以用替换反序列化的对象
不同的对象或代理。自定义的反序列化过程发生在对象期间
在对象返回到应用程序并转换为预期类型之前进行重建。到......的时候
开发人员尝试强制执行预期类型,代码可能已经执行。
自定义反序列化例程在可序列化 classes 中定义,需要出现在
运行time classpath 并且不能被攻击者注入因此这些攻击的可利用性取决于
应用程序环境中可用的 classes。不幸的是,常见的第三方 classes 甚至
JDK classes 可被滥用以耗尽 JVM 资源、部署恶意文件或 运行 任意代码。
某些协议在传输层的幕后使用 Java 序列化。 RMI 和 JMX 是
这些协议的示例。
示例 1:这是一个可以公开公开的 RMI 接口示例,包含方法
一个或多个参数。当远程调用这些方法时,参数将在
服务器允许攻击者注入恶意对象图。
public interface MyService extends java.rmi.Remote {
public Object doSomething (Object arg0) throws RemoteException;
public Object doSomethingElse (Object arg0, Object arg1) throws
RemoteException;
...
}
示例 2:JMX MBeans 还使用 Java 序列化来传输调用参数。在下面的示例中,
MyManagedBean class 方法将向客户端公开。
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("com.example:type=MyManagedBean");
MyManagedBean mbean = new MyManagedBean();
mbs.registerMBean(mbean, name);
推荐
如果可能,不要在未验证对象流内容的情况下反序列化不受信任的数据。为了
验证 classes 被反序列化,应该使用前瞻反序列化模式。
对象流将首先包含 class 描述元数据,然后是它们的序列化字节
成员字段。 Java 序列化过程允许开发人员阅读 class 描述并决定
是继续对象的反序列化还是中止它。为此,有必要
subclass java.io.ObjectInputStream 并提供自定义实现
resolveClass(ObjectStreamClass desc) 方法,其中 class 验证和验证应该
2016 年 9 月 29 日,5:09 下午
版权所有 2015 惠普
企业发展 LP 13
发生。
存在可以轻松使用的前瞻模式的现有实现,例如 Apache
公共 IO (org.apache.commons.io.serialization.ValidatingObjectInputStream)。
始终使用严格的白名单方法来仅反序列化预期类型。黑名单方法不是
推荐,因为攻击者可以使用许多可用的小工具来绕过黑名单。另外,请记住
尽管一些 classes 实现代码执行是众所周知的,但可能还有其他一些是
未知或未公开,因此始终首选白名单方法。白名单中允许的任何 class
应该进行审核以确保反序列化是安全的。
为避免拒绝服务攻击,建议您重写 resolveObject(Object
obj) 方法来计算有多少对象被反序列化,并在以下情况下中止反序列化
超过了一个阈值。
当反序列化发生在库或框架中时(例如,当使用 JMX、RMI、JMS、HTTP 调用程序时)
上述建议没有用,因为它超出了开发人员的控制范围。在这些情况下,您可以
希望确保这些协议满足以下要求:
- 不公开曝光。
- 使用身份验证。
- 使用完整性检查。
- 使用加密。
此外,HPE Security Fortify Runtime 提供每次执行的安全控制
应用程序从 ObjectInputStream 执行反序列化,保护两个应用程序代码,但
还有来自此类攻击的库和框架代码。
看看ValidatingObjectInputStream。基本上,您将允许反序列化的 类 列入白名单(您应该根据获取的信息了解这些)。然后验证器将检查序列化数据的元数据并拒绝任何不在白名单内的类。
如果您查看建议中的提示部分,它指出即使实现了前瞻性 ObjectInputStream,也会报告该问题。因此,即使您能够解决问题,您也不会摆脱发现。
但是,看起来您的代码正在使用 JMS,而对于 JMS,您无法控制反序列化。您复制和粘贴的建议表明了这一点:
When deserialization takes place in library, or framework (e.g. when using JMX, RMI, JMS, HTTP Invokers) the above recommendation is not useful since it is beyond the developer's control. In those cases, you may want to make sure that these protocols meet the following requirements:
- Not exposed publicly.
- Use authentication.
- Use integrity checks.
- Use encryption.
因此,您真正的解决办法是确保遵循这四个要点。您将必须对您的连接进行研究,并且根据您的要求和限制,这可能是不可能的。
万一有人访问这个旧的 post,我通过从 ObjectMessage 切换到 TextMessage 并只发送 JSON 而不是序列化对象来解决这个问题。
This post was very informative
//Sender
ObjectMapper mapper = new ObjectMapper();
TextMessage message = session.createTextMessage(mapper.writeValueAsString(foo));
messageBus.send(message);
//Receiver
ObjectMapper mapper = new ObjectMapper();
try {
Foobar foo= mapper.readValue(textMessage.getText(), new TypeReference<Foobar>(){});
dataHandlerProcess(foo);
} catch (IOException e) {
logger.error("Could not parse Foobar JSON ",e );
return;
}
在我的代码中,我从从 JMS 队列接收到的 ObjectMessage 对象调用 getObject() 方法。 Fortify 报告抱怨此 getObject() 方法的错误名称如下 Dynamic Code Evaluation: Unsafe Deserialization
。
基本上它说,我不应该在不验证对象流的内容的情况下反序列化不受信任的数据。下面是代码。
请问我应该如何以及使用什么方法来摆脱这个 Fortify 报告错误。
if (message instanceof ObjectMessage) {
ObjectMessage objMessage = (ObjectMessage) message;
Object objReportMessage = objMessage.getObject();
....
这是 Fortify 报告的问题和建议。然后它将此错误指向上面的代码 objMessage.getObject();
动态代码评估:不安全的反序列化(1 期)
摘要 在 运行 时间反序列化用户控制的对象流可以允许攻击者在 服务器,滥用应用程序逻辑或导致拒绝服务。
说明 Java 序列化将对象图转换为包含对象本身和 必要的元数据从字节流中重建它们。开发人员可以创建自定义代码来帮助 在反序列化 Java 对象的过程中,他们甚至可以用替换反序列化的对象 不同的对象或代理。自定义的反序列化过程发生在对象期间 在对象返回到应用程序并转换为预期类型之前进行重建。到......的时候 开发人员尝试强制执行预期类型,代码可能已经执行。 自定义反序列化例程在可序列化 classes 中定义,需要出现在 运行time classpath 并且不能被攻击者注入因此这些攻击的可利用性取决于 应用程序环境中可用的 classes。不幸的是,常见的第三方 classes 甚至 JDK classes 可被滥用以耗尽 JVM 资源、部署恶意文件或 运行 任意代码。 某些协议在传输层的幕后使用 Java 序列化。 RMI 和 JMX 是 这些协议的示例。
示例 1:这是一个可以公开公开的 RMI 接口示例,包含方法 一个或多个参数。当远程调用这些方法时,参数将在 服务器允许攻击者注入恶意对象图。
public interface MyService extends java.rmi.Remote {
public Object doSomething (Object arg0) throws RemoteException;
public Object doSomethingElse (Object arg0, Object arg1) throws
RemoteException;
...
}
示例 2:JMX MBeans 还使用 Java 序列化来传输调用参数。在下面的示例中, MyManagedBean class 方法将向客户端公开。
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("com.example:type=MyManagedBean");
MyManagedBean mbean = new MyManagedBean();
mbs.registerMBean(mbean, name);
推荐 如果可能,不要在未验证对象流内容的情况下反序列化不受信任的数据。为了 验证 classes 被反序列化,应该使用前瞻反序列化模式。 对象流将首先包含 class 描述元数据,然后是它们的序列化字节 成员字段。 Java 序列化过程允许开发人员阅读 class 描述并决定 是继续对象的反序列化还是中止它。为此,有必要 subclass java.io.ObjectInputStream 并提供自定义实现 resolveClass(ObjectStreamClass desc) 方法,其中 class 验证和验证应该 2016 年 9 月 29 日,5:09 下午 版权所有 2015 惠普 企业发展 LP 13 发生。 存在可以轻松使用的前瞻模式的现有实现,例如 Apache 公共 IO (org.apache.commons.io.serialization.ValidatingObjectInputStream)。 始终使用严格的白名单方法来仅反序列化预期类型。黑名单方法不是 推荐,因为攻击者可以使用许多可用的小工具来绕过黑名单。另外,请记住 尽管一些 classes 实现代码执行是众所周知的,但可能还有其他一些是 未知或未公开,因此始终首选白名单方法。白名单中允许的任何 class 应该进行审核以确保反序列化是安全的。 为避免拒绝服务攻击,建议您重写 resolveObject(Object obj) 方法来计算有多少对象被反序列化,并在以下情况下中止反序列化 超过了一个阈值。 当反序列化发生在库或框架中时(例如,当使用 JMX、RMI、JMS、HTTP 调用程序时) 上述建议没有用,因为它超出了开发人员的控制范围。在这些情况下,您可以 希望确保这些协议满足以下要求: - 不公开曝光。 - 使用身份验证。 - 使用完整性检查。 - 使用加密。 此外,HPE Security Fortify Runtime 提供每次执行的安全控制 应用程序从 ObjectInputStream 执行反序列化,保护两个应用程序代码,但 还有来自此类攻击的库和框架代码。
看看ValidatingObjectInputStream。基本上,您将允许反序列化的 类 列入白名单(您应该根据获取的信息了解这些)。然后验证器将检查序列化数据的元数据并拒绝任何不在白名单内的类。
如果您查看建议中的提示部分,它指出即使实现了前瞻性 ObjectInputStream,也会报告该问题。因此,即使您能够解决问题,您也不会摆脱发现。
但是,看起来您的代码正在使用 JMS,而对于 JMS,您无法控制反序列化。您复制和粘贴的建议表明了这一点:
When deserialization takes place in library, or framework (e.g. when using JMX, RMI, JMS, HTTP Invokers) the above recommendation is not useful since it is beyond the developer's control. In those cases, you may want to make sure that these protocols meet the following requirements:
- Not exposed publicly.
- Use authentication.
- Use integrity checks.
- Use encryption.
因此,您真正的解决办法是确保遵循这四个要点。您将必须对您的连接进行研究,并且根据您的要求和限制,这可能是不可能的。
万一有人访问这个旧的 post,我通过从 ObjectMessage 切换到 TextMessage 并只发送 JSON 而不是序列化对象来解决这个问题。
This post was very informative
//Sender
ObjectMapper mapper = new ObjectMapper();
TextMessage message = session.createTextMessage(mapper.writeValueAsString(foo));
messageBus.send(message);
//Receiver
ObjectMapper mapper = new ObjectMapper();
try {
Foobar foo= mapper.readValue(textMessage.getText(), new TypeReference<Foobar>(){});
dataHandlerProcess(foo);
} catch (IOException e) {
logger.error("Could not parse Foobar JSON ",e );
return;
}