You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

Interfaces are used to group together all the methods that a class promises to publicly expose. The implementing classes are obliged to provide concrete implementations for all these methods. Interfaces are a necessary ingredient of the public API and once released, it can be very hard to remediate any flaws without breaking any code that implements the older version. By far, the interface changes resulting from security fixes can severely impair the contract of the implementing classes.

Noncompliant Code Example

In this example, an interface User was frozen with two methods authenticate() and subscribe(). No later, the providers released a free service that did not rely on authentication. The addition of the freeService() method, unfortunately, broke all the client code that implemented the interface.

public interface User {
  boolean authenticate(String username, char[] password);
  void subscribe(int noOfDays);
  void freeService(); // introduced after the class is publicly released
}

Compliant Solution

A better design strategy would have been to anticipate the future evolution of the service. The core functionality should have been implemented in the User interface and in this case, only the premium service may be required to extend from it. To avail of the new free service, an existing class could then choose to simply implement the new interface FreeUser or just completely ignore it.

public interface User {
  boolean authenticate(String username, char[] password);
}

public interface PremiumUser extends User {
  void subscribe(int noOfDays);
}

public interface FreeUser {
  void freeService();
}

Compliant Solution

An alternate recommendation is to prefer abstract classes for dealing with constant evolution, but this comes at the cost of the loss in flexibility that interfaces offer. A preferable pattern is for the provider to distribute an abstract skeletal class that implements the evolving interface. The skeletal class can selectively implement a few methods and force the extending classes to provide concrete implementations of the others. If a new method is added to the interface, the skeletal class can provide a non-abstract default implementation that the extending class can optionally override. Thus, the new method need not be explicitly implemented by the class.

public interface User {
  boolean authenticate(String username, char[] password);
  void subscribe(int noOfDays);
  void freeService(); // introduced after the class is publicly released
}

abstract class SkeletalUser implements User {
  public abstract boolean authenticate(String username, char[] password);
  public abstract void subscribe(int noOfDays);
  public void freeService() { /* added later, provide implementation and re-release class*/ }
}

class Client extends SkeletalUser {
  // implements authenticate and subscribe()
}

Risk Assessment

Failing to publish stable, flaw-free interfaces can break the contracts of the implementing classes.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

MSC09-J

low

probable

high

P2

L3

References

[[Bloch 08]] Item 18: "Prefer interfaces to abstract classes"


MSC05-J. Make sensitive classes noncloneable      49. Miscellaneous (MSC)      MSC30-J. Generate truly random numbers

  • No labels