Java Serialization: How Your Applications Persist Data

SanthoshSanthosh
5 min read

Have you ever wondered how a game's state or user settings are saved even after restarting the application? The answer lies in serialization! In fact, more than 70% of enterprise applications require some form of data persistence, making serialization a crucial aspect of application design.


Java Serialization: Serialization is the process of converting an object into a byte stream for storage or transmission. In Java, to serialize an object, the class must implement the Serializable interface. This allows the object’s state to be converted into a format that can be saved to a .ser file or transmitted over a network, ensuring platform independence.

  • In java, in order to serialize an Object in java, the Class needs to implement "Serializable interface".

  • A Child class of a Parent class which implements Serializable interface will do so as well.

  • The serialized bytes can be stored in .ser file. This file is platform independent or they can be sent over a network to another computer.

Steps to Serialize and deserialize in Java with example:

package Serialization;

import java.io.Serializable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

// A class to demonstrate Serialization
class Employee implements Serializable
{
    String name;
    transient long salary; // Employee's salary is a sensitive data, hence it is declared transient
    Employee(String name, long salary)
    {
        name = _name;
        salary = _salary;
    }
}

public class SerializationExample
{
    public static void main(String[] args) throws Exception
    {
        Employee employee1 = new Employee("Charlie", 300000);
        Employee deSerializedObject = null; // do not instantiate the object which needs to be deserialized
        // serialization
        FileOutputStream fOut = new FileOutputStream(".\\filePath");
        ObjectOutputStream objOut = new ObjectOutputStream(fOut);
        objOut.writeObject(employee1);
        fOut.close(); objOut.close();

        // deserialization
        FileInputStream fIn = new FileInputStream(".\\filePath");
        ObjectInputStream objIn = new ObjectInputStream(fIn);

        // should typeCaste the deserialized object
        deSerializedObject = (Employee) objIn.readObject();
        String name = deSerializedObject.name;

        // since salary is declared transient in Employee class, it won't be serialized hence null will be returned
        long salary = deSerializedObject.salary;
        System.out.println("Name: " + name + " salary: " + salary);
        fIn.close(); objIn.close();
    }
}

transient keyword:

The transient keyword in Java is used to indicate that a particular field should not be serialized when an object is converted into a byte stream. During serialization, fields marked as transient are ignored, which is useful for sensitive data (like passwords) or fields that can be recalculated or restored (like temporary states) that do not need to be saved. This helps in controlling what data is persisted and enhances security by preventing sensitive information from being serialized.

Static fields:

The fields which are declared static are not serialized since they are properties of a class and not the Object.

serialVersionUID:

it is a unique identifier for each class that implements the Serializable interface in Java. It is used during the deserialization process to verify that the sender and receiver of a serialized object have loaded classes that are compatible with respect to serialization; if the serialVersionUID does not match, a InvalidClassException is thrown. This helps ensure that the serialized data can be safely restored to an object of the correct version.

Security Risks in Serialization:

while serialization is a powerful tool for persistence and data exchange, can also introduce security vulnerabilities if not handled carefully.

Deserialization Attacks:

These attacks occur when an attacker crafts a malicious payload that, when deserialized, executes unintended code or modifies the application’s state. This can lead to unauthorized access, data breaches, or even remote code execution.

Example: If an application deserializes objects without validating their integrity, an attacker could send a manipulated serialized object that exploits vulnerabilities in the application.

Information Leakage:

Serialized data might inadvertently contain sensitive information (like passwords or personal data). If this data is exposed (e.g., through logs or insecure storage), it could lead to privacy violations or security breaches.

Class Loading Vulnerabilities:

During deserialization, the JVM may load classes that could be tampered with or maliciously crafted, leading to potential vulnerabilities or exploits.

Mitigating Serialization Risks in Java:

Validating Incoming Data:

Checksums and Digital Signatures: Employ checksums or digital signatures to verify the data's integrity and authenticity. This ensures that the data has not been tampered with during transmission or storage.

Data Structure and Type Validation: Ensure that the serialized data conforms to the expected structure and class types. This helps prevent unexpected or malicious objects from being deserialized.

Secure Serialization Libraries

Specialized Libraries: Opt for libraries designed with security in mind. These libraries often incorporate features to prevent common vulnerabilities like deserialization attacks.

Examples: Consider using libraries like Gson or Jackson for JSON serialization, which are generally more secure than Java's built-in serialization mechanisms.

Limiting Serialization Exposure

Transient Fields: Mark sensitive fields as transient to exclude them from the serialization process. This prevents unauthorized access to critical data.

Custom Serialization Methods: Implement custom writeObject and readObject methods to exert granular control over what gets serialized.

Stronger Classloaders

Whitelisting: Use custom classloaders to restrict which classes can be loaded during deserialization. This helps prevent unauthorized or malicious code execution.

Controlled Loading: Implement a whitelisting approach to allow only specific, trusted classes to be deserialized.

Join the Conversation!

I’d love to hear your thoughts and experiences with Java serialization. Have you encountered any challenges or interesting use cases? What libraries do you prefer for serialization, and why? Share your insights in the comments below!

connect with me through LinkedIn!

Links to Java documentation and Geeks for Geeks tutorial:

1. https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html

2. https://www.geeksforgeeks.org/serialization-in-java/

1
Subscribe to my newsletter

Read articles from Santhosh directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Santhosh
Santhosh

I’m a software developer passionate about exploring new technologies and continuously learning. On my blog, I share what I discover—whether it’s cool tricks, coding solutions, or interesting tools. My goal is to document my journey and help others by sharing insights that I find useful along the way. Join me as I write about: Programming tips & tricks Lessons from everyday coding challenges Interesting tools & frameworks I come across Always curious, always learning—let’s grow together! 🚀