CLS01-G: Limit Accessibility of Fields
Introduction
Controlling who or what can access and change certain parts of a code is crucial because if all variables can be freely changed, it can lead to chaos, especially when many parts of the code try to change them simultaneously. For example, fields that are too open to changes, like public ones or those referencing mutable objects, can get messed up if multiple threads try to use them simultaneously. (These fields may also be corrupted by multiple threads accessing them concurrently [Bloch 2008].)
Not limiting access to these fields opens the door to problems like data corruption or security breaches. Even when we try to limit access, it's not always foolproof. Some clever tricks, like using reflection, can still bypass these restrictions. So, while controlling field accessibility is important, we also need to think about additional ways to protect our code from unwanted changes or attacks.
Non-compliant Code Example (Public Primitive Field)
In this non-compliant code example, the total
field tracks the total number of elements added to and removed from a container using the increment and decrement methods, respectively.
// Noncompliant Code Example
public class Widget {
public var total: int
function increment() : void {
if (total < Integer.MAX_VALUE) {
total++
} else {
throw new ArithmeticException("Overflow")
}
}
function decrement() : void {
if (total > 0) {
total--
} else {
throw new ArithmeticException("Overflow")
}
}
}
As a public field, total
can be altered by client code independently of the increment
and decrement
methods.
Compliant Solution (Property)
Accessor methods provide controlled access to fields outside the package in which their class is declared. This compliant solution defines the total
property and stores its value in the _total
variable using variable alias syntax. The increment
and decrement
methods modify its value while preserving class invariants.
// Compliant Solution
public class Widget {
private var _total: int as readonly Total
function increment() : void {
if (total < Integer.MAX_VALUE) {
total++
} else {
throw new ArithmeticException("Overflow")
}
}
function decrement() : void {
if (total > 0) {
total--
} else {
throw new ArithmeticException("Overflow")
}
}
}
If you need to perform additional processing before storing or retrieving property values, you can write either or both getter and setter methods for the property.
Non-compliant Code Example (Public Final Reference Field)
Programmers often incorrectly assume that declaring a field or variable final
makes the referenced object immutable. Declaring variables with a primitive type final
prevents changes to their values after initialization (by standard Gosu processing). However, declaring the field final only makes the reference immutable when the field has a reference type. The final
modifier does not affect the referenced object.
This non-compliant code example declares a final reference to a mutable Helium object with public accessibility:
// Noncompliant Code Example
class Element {
private var _atomicNo: int as AtomicNo = 1
construct(atomicno : int) {
_atomicNo = atomicno
}
function decay() : int {
_atomicNo--
return _atomicNo
}
}
public class Widget {
public final var Helium: Element = new Element(2)
}
var w = new Widget()
print(w.Helium.AtomicNo)
w.Helium.decay()
print(w.Helium.AtomicNo)
Code instantiating the Widget class can read the reference to the Helium
object and invoke the decay
method on the object, decreasing its atomic number by one (producing a Hydrogen atom).
Non-compliant Code Example (Read-only Reference Property)
While the Gosu readonly
modifier can declare Helium as non-assignable, internally the _helium
reference remains mutable. Adding final to _helium
makes the reference immutable. However, when a property has a reference type, declaring the property readonly
only makes the reference itself read-only. The readonly
modifier does not affect the referenced object.
This non-compliant code example declares a final, read-only mutable property that provides a reference to a Helium object:
// Noncompliant Code Example
public class Widget {
private final var _helium: Element as readonly Helium = new Element(2)
}
Declaring a reference as a readonly
property still allows code that instantiates the Widget
class to modify the Helium
object.
Compliant Solution (Explicit Getter Method)
This compliant solution defines an explicit getter method for the Helium property:
// Compliant Solution
property get Helium() : Element {
return new Element(_helium)
}
This getter method invokes the copy constructor for the Element class to produce a copy of the Helium
object:
// Compliant Solution
class Element {
private var _atomicNo: int as AtomicNo = 1
// copy constructor
construct(elem : Element) {
_atomicNo = elem._atomicNo
}
// . . .
}
The following code now calls the decay method on a copy of the Helium
object and does not alter the state of the Widget
object:
var w = new Widget()
print(w.Helium.AtomicNo)
w.Helium.decay()
print(w.Helium.AtomicNo)
Ensure that you do not return references to private mutable objects from accessor methods (see OBJ05-J. Do not return references to private mutable class members for details).
Non-compliant Code Example (Public Final Array)
A nonzero-length array is always mutable. Declaring a public final array is a potential security risk as a client may modify the array elements.
// Noncompliant Code Example
public class Widget {
private final var _rgb: String[] as readonly RGB = {"red", "green", "blue"}
}
var w = new Widget()
w.RGB[0] = "purple"
print(Arrays.toString(w.RGB))
Declaring the array reference final prevents modification of the reference but does not prevent clients from modifying the array's contents.
Compliant Solution (Index Getter)
This compliant solution declares a private array and provides public methods to get individual items and array size:
// Compliant Solution
class IndexGetter {
private static final var items:String[] = {/* ... */}
static function retrieveItem(index:int) : String {
return item[index]
}
}
Providing direct access to the array objects themselves is safe because String
is immutable.
Compliant Solution (Unmodifiable Wrappers)
This compliant solution constructs an immutable public list from the private array. Thus, it is safe to share immutable objects without the risk that the recipient can modify them [Mettler 2010]. This example is safe because String is immutable.
// Compliant Solution
uses java.util.Arrays
uses java.util.Collections
class UnmodifiableWrapper {
private static final var items:String[] = {/* ... */}
static function retrieveItemList() : List<String> {
return Collections.unmodifiableList(Arrays.asList(items))
}
}
A client can modify neither the original array values nor the public list. For more details about unmodifiable wrappers, refer to OBJ56-J. Provide sensitive mutable classes with unmodifiable wrappers.
Risk Assessment
Failing to limit field accessibility can defeat encapsulation, allow attackers to manipulate fields to violate class invariants, or allow these fields to be corrupted due to concurrent accesses from multiple threads.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
CLS01-G | Medium | Highly Likely | Medium | L12 | L1 |
Automated Detection
Detection of public and protected fields is trivial; heuristic detection of the presence or absence of accessor methods is straightforward. However, simply reporting all detected cases without suppressing those cases covered by the exceptions to this rule would produce excessive false positives. Sound detection and application of the exceptions to this rule are infeasible; however, heuristic techniques may be helpful.
Additional Resources
- Gosu Secure Coding Guidelines
- SEI CERT Oracle Coding Standard for Java
- ISO/IEC TR 24772:2019 - Injection [RST]
- MITRE CWE
- Core Java™ 2, Volume I, Fundamentals
- Effective Java, 3rd Edition
- Item 15, "Minimize the Accessibility of Classes and Members"
- Item 16, "In Public Classes, Use Accessor Methods, Not Public Fields"
- Secure Coding Guidelines for Java SE
- Guideline 6-8 / MUTABLE-8: Define wrapper methods around the modifiable internal state
- Gosu - Getting Started - Properties
- Gosu Reference - Defining properties
Was this page helpful?