Versions Compared

Key

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

Serialization and deserialization can be used to bypass security manager checks. A serializable class may employ security manager checks in its constructors for various reasons. For example, the checks prevent an attacker from modifying the internal state of the class.

Non-Compliant Code Example

In this non-compliant example, security manager checks are used within the constructor but are not replicated throughout, specifically, within the readObject and writeObject methods that are used in the serialization-deserialization process. This allows an attacker to maliciously create instances of the class that bypass security manager checks when deserialization is performed.

Code Block
bgColor#FFcccc
public final class CreditCard implements java.io.Serializable {

  //private internal state
  private String credit_card;
  private static final String DEFAULT = "DEFAULT";

  public CreditCard() {
    //initialize credit_card to default value
    credit_card = DEFAULT;
  }

  //allows callers to modify (private) internal state
  public void changeCC(String newCC) {
    if (credit_card.equals(newCC)) {
      // no change
      return;
    } else {  
      validateInput(newCC);
      credit_card = newCC;
    }
  }

  // readObject correctly enforces checks during deserialization
  private  readObject(java.io.ObjectInputStream in) {
    defaultReadObject();
    // if the deserialized name does not match the default value normally
    // created at construction time, duplicate the checks
    if (!DEFAULT.equals(credit_card)) {
      validateInput(credit_card);
    }
  }

  // allows callers to retrieve internal state
  public String getValue() {
    return somePublicValue;
  }

  // writeObject correctly enforces checks during serialization
  private void writeObject(java.io.ObjectOutputStream out) {
    out.writeObject(credit_card);
  }
}

Compliant Solution

The compliant solution correctly implements security manager checks in all constructors, methods that can modify internal state and methods that retrieve internal state. As a result, an attacker cannot create an instance of the object with modified state (using deserialization) and cannot simply read the serialized byte stream to uncover sensitive data (using serialization).

Code Block
bgColor#ccccff
public final class SecureCreditCard implements java.io.Serializable {

  //private internal state
  private String credit_card;
  private static final String DEFAULT = "DEFAULT";

  public SecureCreditCard() {
    //initialize credit_card to default value
    credit_card = DEFAULT;
  }

  //allows callers to modify (private) internal state
  public void changeCC(String newCC) {
    if (credit_card.equals(newCC)) {
      // no change
      return;
    } else {
      // check permissions to modify credit_card
      performSecurityManagerCheck();
      validateInput(newCC);
      credit_card = newCC;
    }
  }

  // readObject correctly enforces checks during deserialization
  private  readObject(java.io.ObjectInputStream in) {
    defaultReadObject();
    // if the deserialized name does not match the default value normally
    // created at construction time, duplicate the checks
    if (!DEFAULT.equals(credit_card)) {
      performSecurityManagerCheck();
      validateInput(credit_card);
    }
  }

  // allows callers to retrieve internal state
  public String getValue() {
  // check permission to get value
    securityManagerCheck();
    return somePublicValue;
  }

  // writeObject correctly enforces checks during serialization
  private void writeObject(java.io.ObjectOutputStream out) {
    // duplicate check from getValue()
    securityManagerCheck();
    out.writeObject(credit_card);
  }
}

References

Sun Secure Coding Guidelines