Many methods offer invariants, which are guarantees made about what the method can do, and what state the object must be in when the method completes. For instance, the %
operator, which computes the remainder of a number provides the invariant that
0 <= abs(a % b) <= abs(b), for all integers a, b where b != 0
Many classes also offer invariants, which are guarantees made about the state of their objects' fields upon the completion of all their methods. For instance, classes whose member fields may not be modified once they have assumed a value are called immutable classes. An important consequence of immutability is that the invariants of instances of these classes are preserved throughout their lifetimes.
A fundamental principle of object-oriented design is that when a subclass extends a superclass, the subclass's methods should preserve the invariants provided by the superclass. Unfortunately, attackers are not constrained by design principles. They can construct malicious classes which extend benign classes and provide methods that violate the invariants of the benign classes.
For instance, if an immutable class is not declared final
, it might be possible to write a malicious subclass capable of changing the state of the immutable object. It would definitely be possible for the malicious subclass to impersonate the immutable object without actually being immutable. Such malicious subclasses would be capable of violating program invariants on which clients depend, thus introducing security vulnerabilities.
Therefore, classes with invariants that other code depends on ought to be declared final
, thereby preventing malicious subclasses from subverting their invariants. Furthermore, immutable classes must be declared final
.
Some classes (hereafter known as superclassesSome classes ("parent classes" hereafter) must permit extension by trusted subclasses while simultaneously preventing extension by untrusted code. Declaring such parent classes Therefore, declaring such superclasses to be final
is not an option, because it would prevent extension by trusted code. Such problems require careful design for inheritance.
Wiki Markup |
---|
Consider two classes belonging to different protection domains; one is malicious, and estends the other, which is a trusted parent class. Consider an object of the malicious classsubclass with a fully qualified invocation of a method defined by the trusted parentsuperclass, and not overridden by the malicious class. In this case, the trusted parent classsuperclass's permissions are examined to execute the method, with the consequence that the malicious object gets the method invoked inside the protection domain of the trusted parent classsuperclass. \[[Gong 2003|AA. Bibliography#Gong 03]\]. |
One commonly suggested solution is to place code at each point where the parent class superclass can be instantiated to ensure that the instance being created has the same type as the parent classsuperclass. When the type is found to be that of a subclass instead of the parent classsuperclass's type, the checking code performs a security manager check to ensure that malicious classes cannot misuse the parent classsuperclass. This approach is insecure because it allows a malicious class to add a finalizer and obtain a partially-initialized instance of the parent classsuperclass. This attack is detailed in guideline OBJ04-J. Do not allow access to partially initialized objects.
...
When the parent class has members that are declared private
or are otherwise inaccessible to the attacker, the attacker must use reflection to exploit those members of the parent class. Declaring the parent class or its methods final
prohibits this level of access.
A method which receives an untrusted, non-final input argument must beware that other methods or threads might modify the input object. Some methods try to prevent modification by making a local copy of the input object. This does not provide sufficient protection, as a shallow copy of an object may still allow it to point to mutable sub-objects, which may still be modified by other methods or threads. Some methods go farther and perform a deep copy of the input object. This mitigates the problem of modifiable sub-objects, but the method might still be passed a mutable object that estends the input object class.
Noncompliant Code Example
...