java 如何在反序列化之前验证对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39799300/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to validate an object before deserializing
提问by Tech Noob
In my code, I am calling getObject() method from an ObjectMessage object received from a JMS queue. Fortify report is complaining about this getObject() method with an error name like this Dynamic Code Evaluation: Unsafe Deserialization
.
Basically it says, I should not deserialize untrusted data without validating the contents of the object stream. Below is the code.
How and what methods I should be using to get rid of this Fortify report error please.
在我的代码中,我从从 JMS 队列接收到的 ObjectMessage 对象调用 getObject() 方法。Fortify 报告抱怨这个 getObject() 方法的错误名称是这样的Dynamic Code Evaluation: Unsafe Deserialization
。基本上它说,我不应该在不验证对象流的内容的情况下反序列化不受信任的数据。下面是代码。我应该如何以及使用什么方法来摆脱这个 Fortify 报告错误。
if (message instanceof ObjectMessage) {
ObjectMessage objMessage = (ObjectMessage) message;
Object objReportMessage = objMessage.getObject();
....
Here is the Fortify reported issue with recommendations. Then it points this error to the code above at the line objMessage.getObject();
这是 Fortify 报告的问题和建议。然后它将此错误指向上面代码中的 objMessage.getObject();
Dynamic Code Evaluation: Unsafe Deserialization (1 issue)
动态代码评估:不安全的反序列化(1 期)
AbstractDeserializing user-controlled object streams at runtime can allow attackers to execute arbitrary code on the server, abuse application logic or lead to denial of service.
Abstract在运行时反序列化用户控制的对象流可以让攻击者在服务器上执行任意代码、滥用应用程序逻辑或导致拒绝服务。
ExplanationJava serialization turns object graphs into byte streams containing the objects themselves and the necessary metadata to reconstruct them from the byte stream. Developers can create custom code to aid in the process of deserializing Java objects, where they may even replace the deserialized objects with different objects, or proxies. The customized deserialization process takes place during objects reconstruction before the objects are returned to the application and cast into expected types. By the time developers try to enforce an expected type, code may have already been executed. Custom deserialization routines are defined in the serializable classes which need to be present in the runtime classpath and cannot be injected by the attacker so the exploitability of these attacks depends on the classes available in the application environment. Unfortunately, common third party classes or even JDK classes can be abused to exhaust JVM resources, deploy malicious files, or run arbitrary code. Certain protocols use Java serialization behind the scenes in the transport layer. RMI and JMX are examples of these protocols.
解释Java 序列化将对象图转换为字节流,其中包含对象本身以及从字节流中重建它们所需的元数据。开发人员可以创建自定义代码来帮助反序列化 Java 对象的过程,他们甚至可以用不同的对象或代理替换反序列化的对象。在对象返回到应用程序并转换为预期类型之前,自定义反序列化过程发生在对象重建期间。当开发人员尝试强制执行预期类型时,代码可能已经被执行。自定义反序列化例程在可序列化类中定义,这些类需要出现在运行时类路径中并且不能被攻击者注入,因此这些攻击的可利用性取决于应用程序环境中可用的类。不幸的是,常见的第三方类甚至 JDK 类都可能被滥用来耗尽 JVM 资源、部署恶意文件或运行任意代码。某些协议在传输层的幕后使用 Java 序列化。RMI 和 JMX 是这些协议的示例。
Example 1: Here is an example of an RMI interface that can be exposed publicly, containing methods with one or more parameters. When invoking these methods remotely, the arguments will be deserialized on the server allowing attackers to inject malicious object graphs.
示例 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;
...
}
Example 2: JMX MBeans also use Java serialization to transmit call arguments. In the example below, the MyManagedBean class methods will be exposed to clients.
示例 2:JMX MBean 也使用 Java 序列化来传输调用参数。在下面的示例中,MyManagedBean 类方法将公开给客户端。
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("com.example:type=MyManagedBean");
MyManagedBean mbean = new MyManagedBean();
mbs.registerMBean(mbean, name);
RecommendationIf possible, do not deserialize untrusted data without validating the contents of the object stream. In order to validate classes being deserialized, the look-ahead deserialization pattern should be used. The object stream will first contain the class description metadata and then the serialized bytes of their member fields. The Java serialization process allows developers to read the class description and decide whether to proceed with the deserialization of the object or abort it. In order to do so, it is necessary to subclass java.io.ObjectInputStream and provide a custom implementation of the resolveClass(ObjectStreamClass desc) method where class validation and verification should Sep 29, 2016, 5:09 PM Copyright 2015 Hewlett Packard Enterprise Development LP 13 take place. There are existing implementations of the look-ahead pattern that can be easily used, such as the Apache Commons IO (org.apache.commons.io.serialization.ValidatingObjectInputStream). Always use a strict whitelist approach to only deserialize expected types. A blacklist approach is not recommended since attackers can use many available gadgets to bypass the blacklist. Also, keep in mind that although some classes to achieve code execution are publicly known, there may be others that are unknown or undisclosed, so a whitelist approach will always be preferred. Any class allowed in the whitelist should be audited to make sure it is safe to deserialize. To avoid denial of service attacks, it is recommended that you override the resolveObject(Object obj) method in order to count how many objects are being deserialized and abort the deserialization when a threshold is surpassed. 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. In addition, HPE Security Fortify Runtime provides security controls to be enforced every time the application performs a deserialization from an ObjectInputStream, protecting both application code but also library and framework code from this type of attack.
推荐如果可能,请不要在未验证对象流内容的情况下反序列化不受信任的数据。为了验证被反序列化的类,应该使用前瞻反序列化模式。对象流将首先包含类描述元数据,然后是其成员字段的序列化字节。Java 序列化过程允许开发人员阅读类描述并决定是继续对对象进行反序列化还是中止它。为此,有必要对 java.io.ObjectInputStream 进行子类化并提供 resolveClass(ObjectStreamClass desc) 方法的自定义实现,其中类验证和验证应为 2016 年 9 月 29 日下午 5:09 版权所有 2015 Hewlett Packard Enterprise Development LP 13 举行。存在可以轻松使用的前瞻模式的现有实现,例如 Apache Commons IO (org.apache.commons.io.serialization.ValidatingObjectInputStream)。始终使用严格的白名单方法仅反序列化预期类型。不推荐使用黑名单方法,因为攻击者可以使用许多可用的小工具来绕过黑名单。此外,请记住,虽然实现代码执行的某些类是公开的,但可能还有其他未知或未公开的类,因此始终首选白名单方法。应审核白名单中允许的任何类,以确保反序列化是安全的。为避免拒绝服务攻击,建议您覆盖 resolveObject(Object obj) 方法,以便计算正在反序列化的对象数量,并在超过阈值时中止反序列化。当在库或框架中进行反序列化时(例如,在使用 JMX、RMI、JMS、HTTP 调用程序时),上述建议没有用,因为它超出了开发人员的控制范围。在这些情况下,您可能需要确保这些协议满足以下要求: - 不公开。- 使用身份验证。- 使用完整性检查。- 使用加密。此外,每次应用程序从 ObjectInputStream 执行反序列化时,HPE Security Fortify Runtime 都会提供安全控制,以保护应用程序代码以及库和框架代码免受此类攻击。当在库或框架中进行反序列化时(例如,在使用 JMX、RMI、JMS、HTTP 调用程序时),上述建议没有用,因为它超出了开发人员的控制范围。在这些情况下,您可能需要确保这些协议满足以下要求: - 不公开。- 使用身份验证。- 使用完整性检查。- 使用加密。此外,每次应用程序从 ObjectInputStream 执行反序列化时,HPE Security Fortify Runtime 都会提供安全控制,以保护应用程序代码以及库和框架代码免受此类攻击。当在库或框架中进行反序列化时(例如,在使用 JMX、RMI、JMS、HTTP 调用程序时),上述建议没有用,因为它超出了开发人员的控制范围。在这些情况下,您可能需要确保这些协议满足以下要求: - 不公开。- 使用身份验证。- 使用完整性检查。- 使用加密。此外,每次应用程序从 ObjectInputStream 执行反序列化时,HPE Security Fortify Runtime 都会提供安全控制,以保护应用程序代码以及库和框架代码免受此类攻击。您可能需要确保这些协议满足以下要求: - 未公开。- 使用身份验证。- 使用完整性检查。- 使用加密。此外,每次应用程序从 ObjectInputStream 执行反序列化时,HPE Security Fortify Runtime 都会提供安全控制,以保护应用程序代码以及库和框架代码免受此类攻击。您可能需要确保这些协议满足以下要求: - 未公开。- 使用身份验证。- 使用完整性检查。- 使用加密。此外,每次应用程序从 ObjectInputStream 执行反序列化时,HPE Security Fortify Runtime 都会提供安全控制,以保护应用程序代码以及库和框架代码免受此类攻击。
采纳答案by Aaron Lawyer
Take a look at the ValidatingObjectInputStream. Basically you whitelist the classes that you will allow to be deserialized (you should know these based on the info you are pulling in). The validator will then check the metadata to the serialized data and reject any classes that are not within the whitelist.
看看ValidatingObjectInputStream。基本上,您将允许反序列化的类列入白名单(您应该根据您获取的信息了解这些)。然后验证器将检查序列化数据的元数据并拒绝任何不在白名单内的类。
回答by wrestang
If you look at the tips portion from the recommendations it states that the issue will be reported even if a look-ahead ObjectInputStream is implemented. Therefore even if you were able to fix the issue, you will not get rid of the finding.
如果您查看建议中的提示部分,它会指出即使实现了超前 ObjectInputStream,也会报告该问题。因此,即使您能够解决问题,您也无法摆脱发现。
However, it looks like your code is using JMS and with JMS, you do not control the deserialization. This is recognized by the recommendations you copied and pasted:
但是,看起来您的代码正在使用 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.
当在库或框架中进行反序列化时(例如,在使用 JMX、RMI、JMS、HTTP 调用程序时),上述建议没有用,因为它超出了开发人员的控制范围。在这些情况下,您可能需要确保这些协议满足以下要求:
- 没有公开曝光。
- 使用身份验证。
- 使用完整性检查。
- 使用加密。
Therefore, your true fix is to ensure those four bullet points are followed. You will have to do research into your connection and, depending on your requirements and limitations, that may not be possible.
因此,您真正的解决方法是确保遵循这四个要点。您必须对您的连接进行研究,根据您的要求和限制,这可能是不可能的。
回答by J. Allen
In case anyone visits this old post, I got around this issue by switching from ObjectMessage to TextMessage and just sending JSON instead of serialized objects.
如果有人访问这篇旧帖子,我通过从 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;
}