Versions Compared

Key

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

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 fix any flaws without breaking any code that implements the older version. By far, the security specific repercussions include:

  • The interface changes resulting from security fixes can severely impair the contracts of the implementing classes. It is even possible that a security fix introduced in a later version is accompanied by modification modifications to an unrelated interface that must now be implemented by the client. This can prevent the client from implementing the security fix since because the new interface would may impose additional implementation burden on it.
  • If an insider crafts changes to an interface or someone accidentally makes modifications, most of the client code that implements the interface will break (Denial of Service)resulting in denial of service. This is particularly pernicious in distributed Java based applications.
  • Implementers can provide skeletal implementations that the clients can directly extend, however, such code can adversely affect the behavior of the subclasses. When these are not provided, the subclasses are forced to provide dummy implementations. This fosters an environment where comments such as 'ignore this code, does nothing', occur incessantly. Such code may never even get tested.
  • If there is a security flaw in a public API (consider ThreadGroups, CON01-J. Avoid using ThreadGroup APIs) it will persist throughout the lifetime of the application.

...

In this example, an interface User was is frozen with two methods authenticate() and subscribe(). Sometime later, the providers released release a free service that did does not rely on authentication. The addition of the freeService() method, unfortunately, broke breaks all the client code that implemented implements the interface. Moreover, the implementers who wished wish to use only freeService had have to face the onus of also providing the other two methods which polluted pollute the API, for reasons discussed earlier.

...

An alternative idea is to prefer abstract classes for dealing with constant evolution, but this comes at the cost of the loss in flexibility that interfaces offer. One notable 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. This pattern is dangerous since because a provider is unaware of the extending class's code and may choose an implementation that will introduce security weaknesses in the client API.

Code Block
bgColor#ffcccc
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(), not freeService()
}

Compliant Solution (1)

A better design strategy would have been is to anticipate the future evolution of the service. The core functionality should have been be 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 may then choose to simply implement the new interface FreeUser or just completely ignore it.

...

Compliant Solution (2)

Another compliant solution is to throw an exception from within the new method defined in the implementing subclass.

...

Although allowable, a less flexible method is to pass on delegate the implementation of the method to subclasses of the client's core interface implementing class.

...