Access control
Table of contents
- Access control
- Classes access modifiers table
- Class members access modifiers table
- Which access modifier should I pick?
Access control
We know that the Random
class produces a pseudo random sequence. This means that a skilled attacker can predict the next number to be drawn after making several observations.
Consider the following example.
package demo;
import java.util.Random;
public class Dice {
public static int roll() {
return RANDOM_GENERATOR.nextInt( 6 ) + 1;
}
public static final Random RANDOM_GENERATOR = new Random(1);
}
The above example makes use of static fields to highlight other problems that may be created when having fields marked as static.
The Random
object is initialised with a seed to simplify the example. Both the roll()
method and the random
static field can be accessed from anywhere. An attacker can take advantage of that and force the next dice roll to be a 6
.
Consider the following example.
package demo;
public class App {
public static void main( final String[] args ) {
/* This method is hacked by an attacker */
initGame();
/* This will always roll a 6 */
playGame();
}
public static void playGame() {
final int a = Dice.roll();
System.out.printf( "I rolled a %s%n", a );
}
public static void initGame() {
/* Skip some numbers so that the next roll, rolls a 6 */
for ( int i = 0; i < 19; i++ ) {
Dice.RANDOM_GENERATOR.nextInt();
}
}
}
Observations
The attacker first called the
nextInt()
method19
times.for ( int i = 0; i < 19; i++ ) { Dice.random.nextInt(); }
The attacker knows that the 20th roll will yield a
6
.The attacker then rolled the dice normally
final int a = Dice.roll(); System.out.printf( "I rolled a %s%n", a );
and obtained the expected the attacker wanted.
I rolled a 6
The attacker can also skip ahead some numbers to make the opponent lose, by rolling a smaller number.
The
Dice
class uses static fields on purpose. Only one instance of thestatic
fieldRANDOM_GENERATOR
exists and anyone can access it from anywhere in the code.
Using access modifies, access to classes and their members (properties, static fields and methods) can be restricted. Making the static field random
, private
, will not allow an attacker to access the static field directly.
package demo;
import java.util.Random;
public class Dice {
public static int roll() {
return RANDOM_GENERATOR.nextInt( 6 ) + 1;
}
private static final Random RANDOM_GENERATOR = new Random( 1 );
}
The attacker cannot now access the random
field.
package demo;
public class App {
public static void main( final String[] args ) {
/* This method is hacked by an attacker */
initGame();
/* This will always roll a 6 */
playGame();
}
public static void playGame() {
final int a = Dice.roll();
System.out.printf( "I rolled a %s%n", a );
}
public static void initGame() {
/* Skip some numbers */
for ( int i = 0; i < 19; i++ ) {
/* ⚠️ Cannot access the private field!! */
Dice.RANDOM_GENERATOR.nextInt();
}
}
}
The following error will be produced
src/main/java/demo/App.java:21: error: RANDOM_GENERATOR has private access in Dice
Dice.RANDOM_GENERATOR.nextInt();
^
Classes access modifiers table
Access Modifier | Accessible From |
---|---|
public | Anywhere |
(no modifier) | same package |
Note that inner classes are class members and thus do not make use of the above table. Inner classes use the table shown in the following section.
Class members access modifiers table
Access Modifier | From Same Class | From Same Package | From Subclass | From Anywhere |
---|---|---|---|---|
public | Yes | Yes | Yes | Yes |
protected | Yes | Yes | Yes | No |
(no modifier) | Yes | Yes | No | No |
private | Yes | No | No | No |
Note that there can be more than one class within the same file. Two or more classes in the same file are considered as classes in the same package.
package demo;
public class A {
public static void printIt() {
System.out.printf( "The value of c is %d%n", B.c );
}
}
class B {
private static final int c = 7;
}
Both classes are defined in the same source file, A.java
, yet these are two different classes within the same package.
$ tree build/classes/java
build/classes/java
└── main
└── demo
├── A.class
└── B.class
There is one exception to this rule. Consider the following example.
package demo;
public class App {
private static final int c = 7;
public static void main( final String[] args ) {
final Runnable r = new Runnable() {
@Override
public void run() {
System.out.printf( "The value of c is %d%n", c );
}
};
r.run();
}
}
An inner anonymous class is created within the App
class.
final Runnable r = new Runnable() {
@Override
public void run() {
System.out.printf( "The value of c is %d%n", c );
}
};
This is compiled as a separate class file, App$1.class
.
$ tree build/classes/java
build/classes/java
└── main
└── demo
├── App$1.class
└── App.class
Despite being a different class within the same package, the inner anonymous class is still allowed to access private
members within the parent class.
More information about access control can be found in this tutorial.
Which access modifier should I pick?
Always start with the least visible access modifier, private
, and then increase the visibility only if required. Remember that once you make something public
it may be impossible to reduce its visibility.