Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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 the equals method.

This example follows the above advice but serialization is still at the mercy of the implementation of the hashcode(

)
method. It is not required to produce a key value (hashcode) that is consistent across different executions of the program or during (de)serialization. Consequently, using the default serialized form of the hashtable is inappropriate. In this noncompliant code example
,
it is not possible to retrieve the value of the object using the original key after deserialization.
 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
bgColor#FFcccc
public class SingletonClass extends Exception {
  public static final SingletonClass INSTANCE = new SingletonClass();
  private SingletonClass() {
Code Block
bgColor#FFcccc

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
bgColor#ccccff
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
bgColor#ccccff

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.

...