Wiki Markup |
---|
According to the Java API \[[API 2006|AA. Bibliography#API 06]\] class {{Object}}, method {{hashcode()}} documentation |
Whenever it is invoked on the same object more than once during an execution of a Java application, the
hashCode
method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
Consequently, the hashcode of an object need not remain consistent across different executions of the application. Similarly, if an object is serialized, its hashcode may not stay consistent with the original value. This introduces several hurdles; for example, upon deserialization of a hashtable, it is impossible to retrieve the contained objects because the corresponding key values may have changedSerialization 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 use the default serialized form for any class with implementation-defined invariants.
Noncompliant Code Example
Wiki Markup |
---|
ThisIn this noncompliant code example uses the {{Key}} class as the key index for the {{Hashtable}}. According to the Java API (based on \[[APIBloch 20062005|AA. Bibliography#APIBibliography#Bloch 0605]\] class {{Hashtable}} documentation |
To successfully store and retrieve objects from a hashtable, the objects used as keys must implement the
hashCode
method and theequals
method.
This example follows the above advice but serialization is still at the mercy of the implementation of the hashcode(
) |
, |
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() { | ||
Code Block | ||
| ||
class Key implements Serializable { // Overrides hashcode and equals methods } class HashSer { public static void main(String[] args) throws IOException, ClassNotFoundException { Hashtable<Key,String> ht = new Hashtable<Key, String>(); Key key = new Key(); ht.put(key, "Value"); System.out.println("Entry: " + ht.get(key)); // Retrieve using the key, works // Serialize the Hashtable object FileOutputStream fos = new FileOutputStream("hashdata.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(ht); oos.close(); // DeserializePerform thesecurity Hashtablechecks object and FileInputStream fis = new FileInputStream("hashdata.ser"); ObjectInputStream ois = new ObjectInputStream(fis); Hashtable<Key, String> ht_in = (Hashtable<Key, String>)(ois.readObject()); ois.close(); if(ht_in.contains("Value")) // Check if the object actually exists in the Hashtableparameter validation } protected int printData() { int System.out.println("Value was found in deserialized object."); data = 1000; if (ht_in.get(key) == null) // Gets printed System.out.println("Object was not found when retrieved using the key."); return data; } } |
Compliant Solution
This compliant solution changes the type of the key value so that it remains consistent across different runs of the program and a multitude of JVMs. This can be achieved by using an Integer
object, for example, to hold the key values.
Code Block | ||
---|---|---|
| ||
class HashSerMalicious { public static void main(String[] args) throws IOException, ClassNotFoundException { Hashtable<Integer, String> htSingletonClass sc = new Hashtable<Integer, String>((SingletonClass) deepCopy(SingletonClass.INSTANCE); htSystem.out.put(new Integer(1), "Value");println(sc == SingletonClass.INSTANCE); // Prints false; indicates new instance System.out.println("Entry:Balance = " + htsc.getprintData(1)); // Retrieve using the key } // SerializeThis themethod Hashtableshould object not be used in FileOutputStreamproduction fos = new FileOutputStream("hashdata.ser");quality code static public ObjectOutputStreamObject oos = new ObjectOutputStream(fos); oos.writeObject(ht);deepCopy(Object obj) { oos.close(); try { // Deserialize the Hashtable object FileInputStream fis ByteArrayOutputStream bos = new FileInputStreamByteArrayOutputStream("hashdata.ser"); ObjectInputStream ois = new ObjectInputStream(fisObjectOutputStream(bos).writeObject(obj); Hashtable<Integer, String>ByteArrayInputStream ht_inbin = (Hashtable<Integer, String>)(ois.readObjectnew ByteArrayInputStream(bos.toByteArray()); ois.close(); return new if(ht_in.contains("Value")) // Check if the object actually exists in the HashtableObjectInputStream(bin).readObject(); } catch (Exception e) { System.out.println("Value was found in deserialized object."throw new IllegalArgumentException(e); } if (ht_in.get(1) == null) // Does not get printed System.out.println("Object was not found when retrieved using the key."); } } |
Compliant Solution
This compliant solution adds a custom readResolve()
method that replaces the deserialized instance with a reference to the appropriate singleton from the current execution. More complicated cases may also require custom writeObject()
or readObject()
methods in addition to (or instead of) the custom readResolve()
method. Note that the custom serialization methods must be declared final
to prevent a malicious subclass from overriding them.
Code Block | ||
---|---|---|
| ||
class SingletonClass extends Exception {
// ...
private final Object readResolve() throws NotSerializableException {
return INSTANCE;
}
}
|
This problem can also be avoided by overriding the equals()
and the hashcode()
method in the Key
class, though it is best to avoid serializing hash tables that are known to use implementation defined parameters.
Risk Assessment
Serializing objects with implementation defined characteristics can corrupt the state of the object.
Guideline | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
SER08-J | low | probable | high | P2 | L3 |
Automated Detection
...
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this guideline on the CERT website.
...