The serialization and deserialization mechanism must respect the accessibility of the implementing class. Untrusted code must be prevented both from writing to the stream using the writeObject()
method and also from creating an instance of an object by calling the readObject()
method. For classes that have constructors, the accessibility of the readObject()
and writeObject()
methods must be less than or equal to the accessibility of the constructor; in general, these methods should be declared to be private
.
Serialization may fail to work as expected even when hostile code lacks access to the serializable class's members. The ObjectInputStream.readObject()
and ObjectOutputStream.writeObject()
methods are declared final
and cannot be overridden. The custom form of serialization involves a mechanism that allows the JVM to detect and use private
implementations of the two methods in the serializable class. The JVM uses default serialization for all classes where the two methods are declared non-private
. This can be insecure from many standpoints, for instance, input validation checks installed in the custom serialized form may be bypassed.
Noncompliant Code Example
This noncompliant code example shows a class Ser
with a private
constructor, indicating that code external to the class should be unable to create instances of it. The class implements java.io.Serializable
and defines public readObject()
and writeObject()
methods. Consequently, untrusted code can obtain the reconstituted objects by using readObject()
, and can write to the stream by using writeObject()
.
public class Ser implements Serializable { private final long serialVersionUID = 123456789; private Ser() { // initialize } public static void writeObject(final ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } public static void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); } }
Similarly, omitting the static
keyword is insufficient to make this example secure; the JVM will fail to detect the two methods, resulting in failure to use the custom serialized form.
Compliant Solution
This compliant solution declares the readObject()
and writeObject()
methods private
and non-static to limit their accessibility.
private void writeObject(final ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); }
Reducing the accessibility also prevents malicious overriding of the two methods.
Risk Assessment
Failure to limit the accessibility of the readObject()
and writeObject()
methods can leave code vulnerable to untrusted invocations.
Guideline |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
SER01-J |
high |
likely |
low |
P27 |
L1 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this guideline on the CERT website.
Bibliography
[[Sun 2006]] "Serialization specification"
[[Ware 2008]]
SER00-J. Maintain serialization compatibility during class evolution 16. Serialization (SER) SER02-J. Extendable classes should not declare readResolve() and writeReplace() private or static