SER12-J. Prevent deserialization of untrusted data
Deserializing untrusted data can cause Java to create an object of an arbitrary attacker-specified class, provided that the class is available on the classpath specified for the JVM. Some classes have triggers that execute additional code when they are created in this manner; see SEC58-J. Deserialization methods should not perform potentially dangerous operations for more information. If such classes are poorly designed, such code could even invoke arbitrary methods, such as Runtime.exec() with an attacker-supplied argument. Therefore, untrusted input to be deserialized should be first validated to ensure that the serialized data contains only trusted classes, perhaps specified in a whitelist of trusted classes. This can be done by overriding the resolveClass() method of the java.io.ObjectInputStream class.
This rule applies only to untrusted data. Data that does not cross a program's trust boundary is, by definition, trusted and can be deserialized without violating this rule. See the exception SER12-EX0 for more information.
Deserialization of data that is trusted but must cross a trust boundary (perhaps because it originates from a different host) automatically complies with this rule, but must also comply with SER02-J. Sign then seal objects before sending them outside a trust boundary for more information.
Non-Compliant Code Example
This non-compliant code deserializes a byte array without first validating what classes will be created. If the object being deserialized belongs to a class with a lax readObject() method, this could result in remote code execution.
import java.io.*;
class DeserializeExample {
public static Object deserialize(byte[] buffer) throws IOException, ClassNotFoundException {
Object ret = null;
try (ByteArrayInputStream bais = new ByteArrayInputStream(buffer)) {
try (ObjectInputStream ois = new ObjectInputStream(bais)) {
ret = ois.readObject();
}
}
return ret;
}
}
Compliant Solution (Look-Ahead Java Deserialization)
This compliant solution is based on http://www.ibm.com/developerworks/library/se-lookahead/ . It inspects the class of any object being deserialized, before its readObject() method is invoked. The code consequently throws an InvalidClassException unless the class of the object (and of all sub-objects) is either a GoodClass1 or a GoodClass2 .
import java.io.*;
import java.util.*;
class WhitelistedObjectInputStream extends ObjectInputStream {
public Set whitelist;
public WhitelistedObjectInputStream(InputStream inputStream, Set wl) throws IOException {
super(inputStream);
whitelist = wl;
}
@Override
protected Class<?> resolveClass(ObjectStreamClass cls) throws IOException, ClassNotFoundException {
if (!whitelist.contains(cls.getName())) {
throw new InvalidClassException("Unexpected serialized class", cls.getName());
}
return super.resolveClass(cls);
}
}
class DeserializeExample {
private static Object deserialize(byte[] buffer) throws IOException, ClassNotFoundException {
Object ret = null;
Set whitelist = new HashSet<String>(Arrays.asList(new String[]{"GoodClass1","GoodClass2"}));
try (ByteArrayInputStream bais = new ByteArrayInputStream(buffer)) {
try (WhitelistedObjectInputStream ois = new WhitelistedObjectInputStream(bais, whitelist)) {
ret = ois.readObject();
}
}
return ret;
}
}
It might appear that the above compliant solution violates Rule OBJ09-J. Compare classes and not class names . However, the security issue addressed by that rule is applicable only when comparing the class of an object that might have been loaded by a foreign ClassLoader , i.e., an object x for which it cannot be guaranteed that x.getClass()==Class.forName(x.getClass().getName()) . In WhitelistedObjectInputStream.resolveClass() (which is the method that does the comparison of class names), a check could be added to verify that the return value (let's call it " ret ") is such that ret == Class.forName(ret.getName()) , but this check would always succeed, so it is pointless to add.
On a somewhat related point, it may be noted that ObjectInputStream.resolveClass() compares the serialVersionUID from the serialized data to the serialVersionUID of the Class object that it is going to return; if there is a mismatch, it throws an exception.
Whitelist
The construction of a suitable whitelist is itself a real challenge. Remote-code-execution exploits have been constructed, not only for classes in the Apache Commons Collection, but also for such core classes as java.util.concurrent.AtomicReferenceArray ( CVE 2012-0507 ), and even simple classes like java.util.HashSet [Terse 2015] and java.net.URL [Terse 2015] can cause a JVM to hang or exhaust memory.
Exceptions
SER12-EX0: Serialized data from a trusted input source does not require sanitization, provided that the code clearly documents that it relies on the input source being trustworthy. For example, if a library is being audited, a routine of that library may have a documented precondition that its callers pre-sanitize any passed-in serialized data or confirm the input source as trustworthy.
Related Vulnerabilities
CERT Vulnerability # 576313 describes a family of exploitable vulnerabilities that arise from violating this rule.
Risk Assessment
Whether a violation of this rule is exploitable depends on what classes are on the JVM's classpath. (Note that this is a property of the execution environment, not of the code being audited.) In the worst case, it could lead to remote execution of arbitrary code.
| Rule | Severity | Likelihood | Detectable | Repairable | Priority | Level |
|---|---|---|---|---|---|---|
| SER12-J | High | Likely | No | No | P9 | L2 |
Automated Detection
| Tool | Version | Checker | Description |
|---|---|---|---|
| CodeSonar | 9.0p0 | JAVA.CLASS.SER.ND | Serialization not disabled (Java) |
| Parasoft Jtest | 2025.2 | CERT.SER12.VOBD | Validate objects before deserialization |
| PVS-Studio | 7.42 | V5333 | |
| Security Reviewer - Static Reviewer | 6.02 | UnsafeDeserialization | Full Implementation |
| Useful for developing exploits that detect violation of this rule |
It should not be difficult to write a static analysis to check for deserialization that fails to override resolveClass() to compare against a whitelist.
Related Guidelines
| MITRE CWE | CWE-502 , Deserialization of Untrusted Data |
Bibliography
[ API 2014 ] | |
| [Terse 2015] | Terse Systems, Closing the Open Door of Java Object Serialization , Nov 8, 2015 |


