Serialization can be used maliciously. Examples include using serialization to maliciously violate the intended invariants of a class. Deserialization is equivalent to object construction; consequently all invariants enforced during object construction must also be enforced during deserialization. The default serialized form lacks any enforcement of class invariants; consequently, it is forbidden to programs must not use the default serialized form for any class with implementation-defined invariants.
The deserialization process creates a new instance of the class without invoking any of the class's constructors. Consequently, any input validation checks present within the constructors are bypassed. Moreover, transient and static fields may fail to reflect their true values because such fields are bypassed during the serialization procedure and consequently cannot be restored from the object stream. As a result any class with transient fields, and any class that performs validation checks in its constructors, must also perform similar valiation validation checks when being deserialized.
Validating deserialized objects helps confirm establishes that the object state is within defined limits and ensures that all transient and static fields have their default safe secure values. Fields that are declared final and contain a constant value will contain the proper value after deserialization, rather than the default value. For example, the value of the field private transient final n = 42
after deserialization will be 42, rather than 0. Deserialization produces default values for all other cases.
...
Wiki Markup |
---|
In this noncompliant code example (based on \[[Bloch 2005|AA. Bibliography#Bloch 05]\]), a class with singleton semantics uses the default serialized form, which fails to enforce any implementation-defined invariants. Consequently, the malicious code can create a second instance even though the class should have only a single instance. For purposes of this example, we assume that the class contains only nonsensitive data. |
Code Block | ||
---|---|---|
| ||
public class SingletonClass extends Exception {
public static final SingletonClass INSTANCE = new SingletonClass();
private SingletonClass() {
// Perform security checks and parameter validation
}
protected int printData() {
int data = 1000;
return data;
}
}
class Malicious {
public static void main(String[] args) {
SingletonClass sc = (SingletonClass) deepCopy(SingletonClass.INSTANCE);
System.out.println(sc == SingletonClass.INSTANCE); // Prints false; indicates new instance
System.out.println("Balance = " + sc.printData());
}
// This method should not be used in production quality code
static public Object deepCopy(Object obj) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
new ObjectOutputStream(bos).writeObject(obj);
ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray());
return new ObjectInputStream(bin).readObject();
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
}
|
...
Note that the class must also be declared final to prevent a malicious subclass from carrying out a finalizer attack. (See rule OBJ11-J. Prevent access to partially initialized objects.) For extendable classes, an acceptable alternative is to use of a flag that indicates whether the instance is safe for use. The flag can be set after validation and must be checked in every method before any operation is performed.
Additionally, any transient or static fields must be explicitly set to an appropriate value within readObject()
.
...
Note that this compliant solution is insufficient to protect sensitive data. See rule SER03-J. Do not serialize unencrypted, sensitive data for additional information.
Compliant Solution (
...
Transient)
This compliant solution marks the fields as transient, so they are not serialized. The readObject()
initializes them using the roll()
method. This class need not be final, as its fields are private and cannot be tampered with by subclasses.
Code Block | ||
---|---|---|
| ||
public class Lottery implements Serializable { private transient int ticket = 1; private transient SecureRandom draw = new SecureRandom(); public Lottery(int ticket) { this.ticket = (int) (Math.abs(ticket % 20000) + 1); } public int getTicket() { return this.ticket; } public int roll() { this.ticket = (int) ((Math.abs(draw.nextInt()) % 20000) + 1); return this.ticket; } public static void main(String[] args) { Lottery l = new Lottery(2); for(int i = 0; i < 10; i++) { l.roll(); System.out.println(l.getTicket()); } } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); this.draw = new SecureRandom(); roll(); } } |
Compliant Solution (
...
Non-serializable)
This compliant solution simply does not mark the Lottery
class serializable.
...
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="84c25c397bc7c06c-bcc3889c-41fe4004-b061ad97-7990a8276669c622a7ecd9ab"><ac:plain-text-body><![CDATA[ | [[MITRE 2009 | AA. Bibliography#MITRE 09]] | [CWE ID 502 | http://cwe.mitre.org/data/definitions/502.html], "Deserialization of Untrusted Data" | ]]></ac:plain-text-body></ac:structured-macro> |
Bibliography
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="5e54e6811ea7efd8-ea9497cc-447f4b8e-b8918bb8-5ad096982a71a8f89846a8a2"><ac:plain-text-body><![CDATA[ | [[API 2006SCG 2009 | AA. Bibliography#API 06Bibliography#SCG 09]] Class | Guideline 5-3 View deserialization the same as object construction | ]]></ac:plain-text-body></ac:structured-macro> |
Bibliography
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="7c60b1b9061888d0-ca791bff-4a07469a-9eb28ae7-346e8bdb7733f584a1f9ceba"><ac:plain-text-body><![CDATA[ | [[Bloch 2008API 2006 | AA. Bibliography#Bloch 08Bibliography#API 06]] | Item 75: "Consider using a custom serialized form" Class | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="15e97aeea5fcaac6-0344b5eb-4f3a4477-bc7492d3-764b93c9576604aeee17fc7b"><ac:plain-text-body><![CDATA[ | [[Greanier 2000Bloch 2008 | AA. Bibliography#Greanier 00Bibliography#Bloch 08]] | Item 75: "Consider using a custom serialized form" | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="77007204846ebd83-6cf42123-4a9d4b92-b15b954e-a10d6057062db399385c9c18"><ac:plain-text-body><![CDATA[ | [[Harold 1999Greanier 2000 | AA. Bibliography#Harold 99Bibliography#Greanier 00]] | Chapter 11: Object Serialization, Validation | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="470473137bd46ebe-52adc626-44f64136-aaa2be04-f6f75f83d87982e85f840bed"><ac:plain-text-body><![CDATA[ | [[Hawtin 2008Harold 1999 | AA. Bibliography#Hawtin 08Bibliography#Harold 99]] | Antipattern 8: Believing deserialisation is unrelated to construction Chapter 11: Object Serialization, Validation | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="08c3d981e946de24-cce897d5-45124b78-be6090cf-82c6ef05e7f8cd31f33d9bd9"><ac:plain-text-body><![CDATA[ | [[SCG 2009Hawtin 2008 | AA. Bibliography#SCG 09Bibliography#Hawtin 08]] | Guideline 5-3 View deserialization the same as object Antipattern 8: Believing deserialisation is unrelated to construction | ]]></ac:plain-text-body></ac:structured-macro> |
...