Serialization is converting objects into byte streams that can be stored, shared, and used by Java machines to reconstruct the objects. Object serialization is essential in Java software development since it allows for data exchange between devices running on different hosts, enforcing platform independence.
Serialization, however, offers an attack surface to hackers as they can edit objects in byte streams or deploy malicious scripts in the runtime environment to create unexpected things. Java serialization vulnerability is often exploited as an entry point for Remote Code Execution and data tampering attacks.
This article discusses serialization vulnerabilities in Java, factors that cause exposure, and strategies to prevent them.
What is Serialization in Java?
Using Java serialization, a user converts the state of objects into a binary format for storing in databases and physical file formats.
Serialization helps Java Integrated Development Environments (IDEs) build a standard format for the objects’ primitive data types to be stored, transmitted, or reconstructed by other Java Virtual Machines. The mechanism also enables infrastructure independence since the standardized format allows data exchange across programming languages and architectures.
How Serialization Works in Java
Java provides an automatic serialization mechanism that implements the java.io.Serializable interface for each class object marked as Serializable. The marker instructs the JVM to write a serializable object into the desired output byte stream using the ObjectOutputStream.writeObject(Object) method. The serializable interface implements a three-step process:
- Writing metadata information of the object’s class
- Recursively write out this class structure’s description until it locates java.lang.object
- Recursively writing out the data associated with the object stream, from the highest serializable fields to the lowest
Serialization Example
Step 1: The default serialization process in Java begins with implementing the Serializable interface in the parent class. Following is a sample class definition:
import java.io.Serializable;
import java.io.Serializable;
class darwin implements Serializable {
int darwinVersion = 10;
}
class contain implements Serializable{
int containVersion = 11;
}
Darwin is now a serializable class.
Step 2: Then call the writeObject() method of the java.io.ObjectOutputStream class. This method serializes the object and writes it to a specified file. The code would look similar to:
public class DarwinTest extends darwin implements Serializable {
int version = 66;
contain con = new contain();
public int getVersion() {
return version;
}
public static void main(String args[]) throws IOException {
FileOutputStream fos = new FileOutputStream("darwin.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
DarwinTest st = new DarwinTest();
oos.writeObject(st);
oos.flush();
oos.close();
}
}
This outputs the serialized stream of bytes to the darwin.out file, whose contents would look similar to:
AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65
73 74 05 52 81 5A AC 66 02 F6 02 00 02 49 00 07
76 65 72 73 69 6F 6E 4C 00 03 63 6F 6E 74 00 09
4C 63 6F 6E 74 61 69 6E 3B 78 72 00 06 70 61 72
65 6E 74 0E DB D2 BD 85 EE 63 7A 02 00 01 49 00
0D 70 61 72 65 6E 74 56 65 72 73 69 6F 6E 78 70
00 00 00 0A 00 00 00 42 73 72 00 07 63 6F 6E 74
61 69 6E FC BB E6 0E FB CB 60 C7 02 00 01 49 00
0E 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E 78
70 00 00 00 0B
Why Do We Need Serialization in Java
Some use-cases for serialization in Java include:
Caching
Object serialization saves the state of an object, whereas deserialization takes little time to recreate the original object compared to building it from a Java class, saving time when accessing objects from cache memory.
Replication
Byte streams can easily be shared across Java Virtual Machines that serialize them in little time. This allows for the provision of multiple object clones across different machines/environments for simple replication.
Multi-tenant communication
Once the original object has been serialized, data structures can be transferred efficiently through a network of machines, allowing for exchanging information between different environments.
Persistence
After applying serialization, Java can store the state of any object in a database or a file system that can be used later.
Cross-machine synchronization
The serialization protocol generates byte streams that work across different architectures and operating systems, allowing for synchronization between different JVMs.

Serialization Vulnerabilities
The serialization process is crucial for Java applications since it provides a standard data structure and enables objects portability. Despite the benefits serialization in Java offers, such applications are susceptible to serialization vulnerabilities where attackers can insert malicious objects into the runtime environment to compromise user or system data. These objects typically include payloads and private methods that run executable code on target JVMs. Once the attacker can control the data being serialized and deserialized, they can control program objects and in-memory variables, influencing the code flow within the application. The application is also susceptible to attacks if the object contains classes whose behavior can be altered during the deserialization process. In such cases, attackers can exploit existing data structures, change their contents, overwrite existing variables and exploit other vulnerabilities within the serialization mechanism.
Insecure Serialization Attacks – Risk Factors
Serialization attacks are less likely to be performed automatically, as plenty of manual work is required to create a malicious object that the serialization mechanism considers valid. Once the hacker has successfully compiled the exploit on one JVM, they can execute malicious code remotely on all other machines hosting the same application. Once attackers gain control over the source code, they assume a complete host takeover of the vulnerable application. In such cases, detecting a successful exploit is highly complex.
Potential factors that cause serialization attacks in Java include:
Deserializing objects from untrusted sources
Most attacks occur since development and operations teams fail to validate user-supplied data before deserializing an object. For applications that rely on the binary serialization format, developers assume that users cannot effectively read and manipulate object data, so they deem all deserialized objects trustworthy. With some effort, an attacker can craft exploits and inject malicious payloads into binary serialized objects, allowing them to abuse the application’s logic.
A large number of dependencies
Typical Java applications/websites implement numerous libraries, each having its dependencies. Therefore, an application comprises many methods and classes that can be difficult to manage securely. This makes it difficult to predict and prevent a serialization attack since a hacker can replicate any class and invoke one method to execute their malicious payload. Hackers can also connect several unexpected method invocations, so the final data parsed into the sink (file system, database, etc.) is entirely different from the initial input. Since developers can not anticipate the flow of data in the application, it is almost impossible to address every security gap opened by the serialization vulnerability.
Java Serialization Attack Approaches
There are multiple ways to exploit insecure serialization vulnerabilities in Java. These include:
Modifying object attributes
If the attacker preserves a valid serialized object while tampering with its static fields, the deserialization process creates a server-side object with altered attribute values. Hackers mainly use this technique to identify and modify user access permissions in HTTP requests, allowing for unauthorized privilege escalation.
Modifying data types
Attackers leverage serialization flaws to supply unexpected data types to abuse Java application logic. In such a case, the hacker builds malicious code that leverages loose comparisons to manipulate the machine into accepting user-supplied data from a deserialized object. The hacker can then use this data to bypass security controls such as authorization and session authentication mechanisms.
Extending application functionality
This approach involves using a Java website/application’s functionality to execute malicious processes on a data set in a deserialized object. This functionality is entirely user-accessible, and the hacker could manually or automatically pass data into risky methods.
Preventing Serialization Attacks in Java
Some best practices to prevent serialization vulnerabilities include:
Use non-native formats
Developers should use standard data formats such as XML and JSON, eliminating the chances of attackers repurposing custom deserialization logic for remote code execution. These non-native formats implement a secure data transfer pattern that creates a separate serialization protocol for objects and state transfer, making the serialization algorithm unavailable to external entities.
Use RASP to prevent remote code execution
Runtime Application Self-Protection (RASP) tools are deployed into application servers to intercept all communications between the host and client machines and detect attacks in real-time. These tools continuously monitor data flows, systems, and user behavior to detect malicious activity. These tools also identify patterns in data structures and serializable objects to build context for regular user and application behavior. Any out-of-context data input is considered a potential threat. RASP tools can be configured to alert teams of attack attempts in diagnostic mode or mitigate the attacks in protection mode.
Implement checks for data integrity/Deserialize signed data
Complement blacklisting and pattern-matching techniques with signature-based positive validation techniques to ensure user-provided data is trustworthy. Additionally, implement sufficient authorization/authentication checks to verify the source of the input byte-stream before deserializing it.
Use custom deserialization methods
OWASP provides a cheat sheet with guidelines on building custom serialization and deserialization code to enumerate safe validation methods. These guidelines can be used to ensure the application is free of untrusted data.
Restrict classes to be serialized by subclassing java.io.ObjectInputStream
The java.io.ObjectInputStream class deserializes objects whose security can be hardened by sub-classing them. This is achieved by overriding the ObjectInputStream.html#resolveClass(), which restricts the classes that can be deserialized. Security teams can also deploy an agent that secures all usage of the java.io.ObjectInputStream class to harden applications against unknown malicious data types.
Use a vulnerability detection tool
Deploy a security testing tool that identifies static and dynamic applications vulnerabilities through continuous scanning and testing. The Crashtest Security Suite helps scan APIs and web applications to identify serialization vulnerabilities before attackers exploit them. Sign up for a free trial to begin a quick and efficient vulnerability scanning.
Java Serialization Video Explanation
Serialization in Java – FAQs
What are the advantages of serialization?
Serialization is built into Java, requiring no third-party integrations to implement. The process is simple to learn, implement and customize for specific use-cases. Serialization also enables Java’s compression, encryption, authentication, and secure computing.
Do serialization vulnerabilities only affect java apps?
Serialization is a global concept, and any language that implements serialization is susceptible to attack vectors. Serialization vulnerabilities also affect Ruby, PHP, and Microsoft .NET applications, among others.