Versions Compared

Key

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

...

In this noncompliant code example [Bloch 2005], a class with singleton semantics uses the default serialized form, which fails to enforce any implementation-defined invariants. Consequently, 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 NumberData extends Number {
  // ...implement abstract Number methods, like Number.doubleValue()...

  private static final NumberData INSTANCE = new NumberData ();
  public static NumberData getInstance() {
    return INSTANCE;
  }

  private NumberData() {
    // Perform security checks and parameter validation
  }

  protected int printData() {
    int data = 1000;
    // print data
    return data;
  }
}

class Malicious {
  public static void main(String[] args) {
    NumberData sc = (NumberData) deepCopy(NumberData.getInstance());
    // Prints false; indicates new instance
    System.out.println(sc == NumberData.getInstance());  
    System.out.println("Balance = " + sc.printData());
  }

  // This method should not be used in production code
  public static 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);
    }
  }
}

...

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.

Code Block
bgColor#ccccff

public class NumberData extends Number {
  // ...
  protected final Object readResolve() throws NotSerializableException {
    return INSTANCE;
  }
}

...

This noncompliant code example uses a custom-defined readObject() method but fails to perform input validation after deserialization. The design of the system requires the maximum ticket number of any lottery ticket to be 20,000, and the minimum ticket number be greater than 0. However, an attacker can manipulate the serialized array to generate a different number on deserialization. Such a number could be greater than 20,000, or could be 0 or negative.

Code Block
bgColor#FFcccc

public class Lottery implements Serializable {	
  private int ticket = 1;
  private 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();
  }
}

...

Any input validation performed in the constructors must also be implemented where ever an object can be deserialized. This compliant solution performs field-by-field validation by reading all fields of the object using the readFields() method and ObjectInputStream.GetField constructor. The value for each field must be fully validated before it is assigned to the object under construction. For more complicated invariants, this may require reading multiple field values into local variables to enable checks that depend on combinations of field values.

Code Block
bgColor#ccccff

public final class Lottery implements Serializable { 
  // ...
  private synchronized void readObject(java.io.ObjectInputStream s)
                       throws IOException, ClassNotFoundException {
    ObjectInputStream.GetField fields = s.readFields();
    int ticket = fields.get("ticket", 0);
    if (ticket > 20000 || ticket <= 0) {
      throw new InvalidObjectException("Not in range!");
    }
    // Validate draw
    this.ticket = ticket;
  }
}

...

This compliant solution marks the fields as transient, so they are not serialized. The readObject() method initializes them using the roll() method. This class need not be final, as its fields are private and cannot be tampered with by subclasses, and its methods have been declared final, to prevent subclasses from overriding and ignoring them.

Code Block
bgColor#ccccff

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 final int getTicket() {
    return this.ticket;
  }

  public final 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();
  }
}

...

This compliant solution simply does not mark the Lottery class serializable.

Code Block
bgColor#ccccff

public final class Lottery {	
  // ...
}

...

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

SER07-J

medium

probable

high

P4

L3

Automated Detection

Tool
Version
Checker
Description
Coverity7.5UNSAFE_DESERIALIZATIONImplemented

Related Guidelines

MITRE CWE

CWE-502, "Deserialization of Untrusted Data"

Secure Coding Guidelines for the Java Programming Language, Version 3.0

Guideline 5-3. View deserialization the same as object construction

...

[API 2006]

Class Object, Class Hashtable

[Bloch 2008]

Item 75, Consider using a custom serialized form

[Greanier 2000]

 

[Harold 1999]

Chapter 11, Object Serialization, Validation

[Hawtin 2008]

Antipattern 8. Believing deserialisation is unrelated to construction

 

      13. Serialization (SER)