Context-Specific Deserialization Filters were introduced in Java 9 to enhance security during object deserialization. These filters allow developers to define custom rules for controlling and restricting the deserialization of objects, helping to prevent vulnerabilities such as deserialization attacks.
What Are Context-Specific Deserialization Filters?
- Purpose:
- Control what types of objects can be deserialized.
- Protect applications from malicious or unintended object deserialization.
- How It Works:
- Filters are applied to streams used in deserialization (
ObjectInputStream). - They can allow, reject, or terminate deserialization based on pre-defined rules.
- Filters are applied to streams used in deserialization (
Key Features
- Granular Control:
- Filters can specify allowed classes, packages, and object hierarchies.
- Integration:
- Compatible with existing serialization APIs like
ObjectInputStream.
- Compatible with existing serialization APIs like
- Configurable:
- Filters can be applied globally or per deserialization stream.
- Flexible Actions:
- Filters can allow, reject, or throw exceptions for disallowed objects.
Example: Basic Usage
Code:
import java.io.*;
public class DeserializationFilterExample {
public static void main(String[] args) throws Exception {
// Define a custom deserialization filter
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"java.util.ArrayList;!*"
);
// Set the global filter
ObjectInputFilter.Config.setSerialFilter(filter);
// Attempt to deserialize an object
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("data.ser"))) {
ObjectInputFilter.Config.setObjectInputFilter(ois, filter);
Object obj = ois.readObject();
System.out.println("Deserialized object: " + obj);
} catch (InvalidClassException e) {
System.err.println("Deserialization rejected: " + e.getMessage());
}
}
}
Explanation of the Example
- Defining the Filter:
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter( "java.util.ArrayList;!*" );- The filter specifies rules:
- Allow deserialization of
java.util.ArrayList. - Reject all other types (
!*).
- Allow deserialization of
- The filter specifies rules:
- Setting a Global Filter:
ObjectInputFilter.Config.setSerialFilter(filter);- Applies the filter globally to all deserialization processes.
- Per-Stream Filter:
ObjectInputFilter.Config.setObjectInputFilter(ois, filter);- Applies the filter to a specific
ObjectInputStreaminstance.
- Applies the filter to a specific
- Handling Rejected Deserialization:
catch (InvalidClassException e) { System.err.println("Deserialization rejected: " + e.getMessage()); }- If an object not allowed by the filter is deserialized, an
InvalidClassExceptionis thrown.
- If an object not allowed by the filter is deserialized, an
- Why Use
!*:- Adding
!*explicitly rejects all types not mentioned in the filter. - Without
!*, some unspecified types may be implicitly allowed depending on JVM implementation and defaults. This can pose a security risk as unintended classes could be deserialized. - Example:
ObjectInputFilter.Config.createFilter("java.util.ArrayList;"); // Implicitly allows other classes in some cases. ObjectInputFilter.Config.createFilter("java.util.ArrayList;!*;"); // Strictly rejects all except ArrayList. - Best Practice: Always include
!*for a secure, default-deny policy to explicitly prevent deserialization of any unexpected types.
- Adding
Updates in Java 17
Java 17 introduced enhancements to deserialization filtering to simplify configuration and strengthen security:
- Global Deserialization Filter via JVM Options:
- Java 17 allows setting a global filter using the
-Djdk.serialFiltersystem property. - Example:
java -Djdk.serialFilter="java.util.ArrayList;!*" -jar MyApplication.jar - This method eliminates the need to modify application code to apply a global filter.
- Java 17 allows setting a global filter using the
- Stricter Defaults:
- Built-in serialization libraries (e.g., RMI) now apply stricter filters by default to reject untrusted or unsupported classes.
- Enhanced Flexibility:
- Developers can now define rules such as
maxdepthandmaxrefsfor more granular control. - Example:
java -Djdk.serialFilter="maxdepth=5;maxrefs=100;java.util.ArrayList;!*"maxdepth: Limits the maximum depth of the object graph during deserialization.maxrefs: Limits the number of references during deserialization.
- Developers can now define rules such as
Example of Enhanced Usage in Java 17:
import java.io.*;
public class DeserializationFilterExample17 {
public static void main(String[] args) throws Exception {
// Set a global filter using system property (Java 17 feature)
System.setProperty("jdk.serialFilter", "java.util.ArrayList;!*");
// Deserialize an object
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("data.ser"))) {
Object obj = ois.readObject();
System.out.println("Deserialized object: " + obj);
} catch (InvalidClassException e) {
System.err.println("Deserialization rejected: " + e.getMessage());
}
}
}
Output Scenarios
- If the Serialized Object is Allowed (
ArrayList):- Example Output:
Deserialized object: [Item1, Item2, Item3]
- Example Output:
- If the Serialized Object is Not Allowed:
- Example Output:
Deserialization rejected: Class <ClassName> is not allowed
- Example Output:
Best Practices
- Define Clear Rules:
- Specify only the classes or packages required by the application.
- Use Global Filters:
- For applications where deserialization is widely used, set a global filter to enforce consistent rules.
- Test Thoroughly:
- Ensure the filter rules cover all expected deserialization scenarios.
- Avoid Deserializing Untrusted Data:
- Minimize reliance on deserialization where possible.
- Include
!*in Filters:- Use
!*to explicitly reject any class not explicitly allowed.
- Use
Advantages
- Enhanced Security:
- Prevents deserialization of unwanted or malicious classes.
- Customizable:
- Filters can be tailored to specific application needs.
- Backward Compatibility:
- Works seamlessly with existing deserialization APIs.
Limitations
- Performance Overhead:
- Additional checks during deserialization may impact performance.
- Complexity:
- Creating comprehensive filters requires detailed knowledge of application data.
Summary
Context-Specific Deserialization Filters provide a robust mechanism to control and secure object deserialization in Java. By using these filters, developers can mitigate risks associated with deserialization attacks and ensure that only safe and intended classes are deserialized. Enhancements in Java 17, such as JVM-level configuration and stricter defaults, make this feature even more powerful and easier to use. Including !* in the filter ensures a secure, default-deny policy, explicitly rejecting any type not allowed by the filter. This feature is particularly valuable in modern applications where security is a critical concern.