The Classes that require special handling during object serialization and deserialization mechanism must respect the accessibility of the implementing class. Untrusted code should not be able to write to the stream using writeObject()
nor should it be able to create an instance of the object by calling the readObject()
method. The accessibility of these methods must match with that of the class constructor (if any); otherwise it should be reduced to private
.
...
implement special methods with exactly the following signatures [API 2014]:
Code Block |
---|
private void writeObject(java.io.ObjectOutputStream out)
throws IOException;
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
private void readObjectNoData()
throws ObjectStreamException;
|
Note that these methods must be declared private for any serializable class. Serializable classes may also implement the readResolve()
and writeReplace()
methods.
According to the Serialization Specification [Sun 2006], readResolve()
and writeReplace()
method documentation:
For Serializable and Externalizable classes, the
readResolve
method allows a class to replace/resolve the object read from the stream before it is returned to the caller. By implementing thereadResolve
method, a class can directly control the types and instances of its own instances being deserialized.For Serializable and Externalizable classes, the
writeReplace
method allows a class of an object to nominate its own replacement in the stream before the object is written. By implementing thewriteReplace
method, a class can directly control the types and instances of its own instances being serialized.
It is possible to add any access-specifier to the readResolve()
and writeReplace()
methods. However, if these methods are declared private, extending classes cannot invoke or override them. Similarly, if these methods are declared static, extending classes cannot override these methods; they can only hide them.
Deviating from these method signatures produces a method that is not invoked during object serialization or deserialization. Such methods, especially if declared public, might be accessible to untrusted code.
Unlike most interfaces, Serializable
does not define the method signatures it requires. Interfaces allow only public fields and methods, whereas readObject()
, readObjectNoData
, and writeObject()
must be declared private. Similarly, the Serializable
interface does not prevent readResolve()
and writeReplace()
methods from being declared static, public, or private. Consequently, the Java serialization mechanism fails to let the compiler identify an incorrect method signature for any of these methods.
Noncompliant Code Example (readObject()
, writeObject()
)
This noncompliant code example shows a class Ser
, which has with a private constructor. This means , indicating that code external to the class should be unable to create its instanceinstances of it. The class implements java.io.Serializable
and defines the public readObject()
and writeObject()
methods. The accessibility of both the methods is public
which allows Consequently, untrusted code to can obtain the reconstituted object (in case of objects by using readObject())
and can write to the stream (in case of by using writeObject()
).
Code Block | ||
---|---|---|
| ||
public class Ser implements Serializable { private static final long serialVersionUID = 123456789; private Ser() { // initializeInitialize } public static void writeObject(final ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } public static void readObject(final ObjectInputStream stream) throws throws IOException, ClassNotFoundException { stream.defaultReadObject(); } } |
...
Note that there are two things wrong with the signatures of writeObject()
and readObject()
in this Noncompliant Code Example: (1) the method is declared public
instead of private
, and (2) the method is declared static
instead of non-static
. Since the method signatures do not exactly match the required signatures, the JVM will not detect the two methods, resulting in failure to use the custom serialized form.
Compliant Solution (readObject()
, writeObject()
)
This compliant solution declares the readObject()
and writeObject()
methods private and nonstatic to limit their accessibility.:
Code Block | ||
---|---|---|
| ||
private void writeObject(final ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } private void readObject(final ObjectInputStream stream) throws throws IOException, ClassNotFoundException { stream.defaultReadObject(); } |
Reducing the accessibility also prevents malicious overriding of the two methods.
Noncompliant Code Example (readResolve()
, writeReplace()
)
This noncompliant code example declares the readResolve()
and writeReplace()
methods as private:
Code Block | ||
---|---|---|
| ||
class Extendable implements Serializable {
private Object readResolve() {
// ...
}
private Object writeReplace() {
// ...
}
}
|
Noncompliant Code Example (readResolve()
, writeReplace()
)
This noncompliant code example declares the readResolve()
and writeReplace()
methods as static:
Code Block | ||
---|---|---|
| ||
class Extendable implements Serializable {
protected static Object readResolve() {
// ...
}
protected static Object writeReplace() {
// ...
}
}
|
Compliant Solution (readResolve()
, writeReplace()
)
This compliant solution declares the two methods protected while eliminating the static
keyword so that subclasses can inherit them:
Code Block | ||
---|---|---|
| ||
class Extendable implements Serializable {
protected Object readResolve() {
// ...
}
protected Object writeReplace() {
// ...
}
}
|
Risk Assessment
Deviating from the proper signatures of serialization methods can lead to unexpected behavior. Failure to limit the accessibility of the readObject()
and writeObject()
methods can leave code vulnerable to untrusted invocations. Declaring readResolve()
and writeReplace()
methods to be static or private can force subclasses to silently ignore them, while declaring them public allows them to be invoked by untrusted code.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
SER01-J |
High |
Likely |
Low | P27 | L1 |
Automated Detection
...
TODO
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
Wiki Markup |
---|
\[[Sun 06|AA. Java References#Sun 06]\] "Serialization specification"
\[[Ware 08|AA. Java References#Ware 08]\] |
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
CodeSonar |
| JAVA.CLASS.SER.ND | Serialization Not Disabled (Java) | ||||||
Coverity | 7.5 | UNSAFE_DESERIALIZATION | Implemented | ||||||
Parasoft Jtest |
| CERT.SER01.ROWO | Ensure that the 'readObject()' and 'writeObject()' methods have the correct signature | ||||||
PVS-Studio |
| V6075 | |||||||
SonarQube |
| S2061 | Custom serialization method signatures should meet requirements |
Related Guidelines
Bibliography
...
SER00-J. Maintain serialization compatibility during class evolution 14. Serialization (SER) SER02-J. Extendable classes should not declare readResolve() and writeReplace() private or static