Avoid Java Serialization!
- November 11, 2020
- 4841 Unique Views
- 2 min read
Serialization in Java allows us to transform an object to a byte stream. This byte stream is either saved to disk or transported to another system. The other way around, a byte stream can be deserialized and allows us to recreate the original object.
The biggest problem is with the deserializing part. Typically it looks something like this:
ObjectInputStream in = new ObjectInputStream( inputStream ); return (Data)in.readObject();
There’s no way to know what you’re deserializing before you decoded it. Possibly, an attacker serialized a malicious object and sent it to your application. Once you call readObject(), the malicious objects have already been instantiated. You might believe that these kinds of attacks are impossible because you need to have a vulnerable class on you classpath. However, if you consider the amount of classes on your classpath—that includes your own code, Java libraries, third-party libraries and frameworks—it is very likely that there is a vulnerable class available.
Java serialization is also called “the gift that keeps on giving” because of the many problems it has produced over the years. Oracle is planning to eventually remove Java serialization as part of Project Amber. However, this may take a while, and it’s unlikely to be fixed in previous versions. Therefore, it is wise to avoid Java serialization as much as possible. If you need to implement serializable on your domain entities, it is best to implement its own readObject(), as seen below. This prevents deserialization.
private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Deserialized not allowed");
}
If you need to Deserialize an inputstream yourself, you should use an ObjectsInputStream with restrictions. A nice example of this is the ValidatingObjectInputStream from Apache Commons IO. This ObjectInputStream checks whether the object that is deserialized, is allowed or not.
FileInputStream fileInput = new FileInputStream(fileName); ValidatingObjectInputStream in = new ValidatingObjectInputStream(fileInput); in.accept(Foo.class); Foo foo_ = (Foo) in.readObject();
Object deserialization problems are not restricted to Java serialization. Deserialization from JSON to Java Object can contain similar problems. An example of such a deserialization issue with the Jackson library is in the blog post “Jackson Deserialization Vulnerability”.
This was just 1 of 10 Java security best practices. Take a look at the full 10 and the easy printable one-pager available.
Don’t Forget to Share This Post!
Comments (2)
Tobiloba
3 years agoI see that you save the point of interest as text in the DB but the response gotten from ChatGPT is JSON. Does this mean you convert the response into string using libraries like gson before saving it in the database?
Denis Magda
3 years agoHey, The response is a String object in the JSON format [1]. The repository takes this JSON string as is and stores to the database [2]. Presently, Spring Data auto-generates the CREATE TABLE statement on the startup and sets the "point of interest" column's type to "text" (or "varchar", don't remember). However, it's always possible to ask Spring Data to use the "json" or "jsonb" type for the column if you wish to query the JSON at the database level. Finally, Vaadin displays a list of PointsOfInterests. Those are generated using the org.json library [3]. Let me know if you have other questions. Hope this helps. [1] https://github.com/YugabyteDB-Samples/budget-journey-gpt/blob/main/src/main/java/com/yugabyte/com/TripsAdvisorService.java#L103 [2] https://github.com/YugabyteDB-Samples/budget-journey-gpt/blob/main/src/main/java/com/yugabyte/com/TripsAdvisorService.java#L74 [3] https://github.com/YugabyteDB-Samples/budget-journey-gpt/blob/main/src/main/java/com/yugabyte/com/TripsAdvisorService.java#L114