Why Enum Singletons Are Safer Than Class Singletons

Class Based Singleton vs Enum Singleton | Security

I’ll discuss two ways of creating a singleton and explain why one is better than the other.

Let’s start with Class Based Singleton. We will create a singleton that should be thread safe by using the keyword synchronized and our singleton instance will be volatile.

Why volatile? The answer is “Double-Checked Locking”!

We want to ensure all threads see the most up-to-date value of the instance from main memory. Without doing so we could have threads seeing an instance is not quite null but also partially initialized.

The code for a class based singleton:

public class ClassBasedSingleton {
private static volatile ClassBasedSingleton plainSingleton;
private ClassBasedSingleton(){
}
public static ClassBasedSingleton getPlainSingleton(){
if(plainSingleton == null){
synchronized (ClassBasedSingleton.class) {
if(plainSingleton == null){
return new ClassBasedSingleton();
}
}
}
return plainSingleton;
}
}

Ok, we achieved our goal. What’s the problem?

The problem is this code is not secure. We want to prevent Reflection Based Instantiation.

Let’s get an instance via reflection using ReflectionBasedInstantiationForClassBasedSingletonTest:

ublic class ReflectionBasedInstantiationForClassBasedSingletonTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 1. Get the Class object for the target class
Class<?> clazz = ClassBasedSingleton.class;
// 2. Get the declared constructor (even if it's private)
Constructor<?> constructor = clazz.getDeclaredConstructor();
// 3. Make the constructor accessible
constructor.setAccessible(true);
// 4. Instantiate the object
ClassBasedSingleton instance = (ClassBasedSingleton) constructor.newInstance();
System.out.println("Instance created: " + instance);
}
}

Output from this code, using the ClassBasedSingleton:

As you can see, we have no errors attempting to access this singleton. Probably not what we want!

Let’s compare this to using an Enum singleton.

Enum singleton code:

public enum EnumSingleton {
SINGLETON;
private int counter = 123;
public synchronized void increment(){
counter++;
}
public int getCounter(){
return counter;
}
}

When we run this through our ReflectionBasedInstantiationForEnumBasedSingletonTest:

public class ReflectionBasedInstantiationForEnumBasedSingletonTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 1. Get the Class object for the target class
Class<?> clazz = EnumSingleton.class;
// 2. Get the declared constructor (even if it's private)
Constructor<?> constructor = clazz.getDeclaredConstructor();
// 3. Make the constructor accessible
constructor.setAccessible(true);
// 4. Instantiate the object
EnumSingleton instance = (EnumSingleton) constructor.newInstance();
System.out.println("Instance created: " + instance);
}
}

this is what we get:

We cannot access the singleton instance using reflection.

Safe!

So what’s happening here?

Java prevents reflection from creating the enum using reflection.

    We can take this a step further and show how Java prevents a 2nd instance creation:

    public class TestEnumSingletonGetSecondInstance {
    public static void main(String[] args){
    EnumSingleton instance1 = EnumSingleton.SINGLETON;
    int counter = instance1.getCounter();
    System.out.println("counter from EnumSingleton: "+counter);
    try {
    // 1. Get the constructor for the enum
    // Note: Enum constructors take (String name, int ordinal)
    Constructor<EnumSingleton> constructor =
    EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
    // 2. Try to bypass the 'private' modifier
    constructor.setAccessible(true);
    // 3. Try to create a NEW instance (This is where it fails!)
    EnumSingleton instance2 = constructor.newInstance("ATTACK_INSTANCE", 1);
    int counter2 = instance2.getCounter();
    System.out.println("counter2 from EnumSingleton: "+counter2);
    } catch (Exception e) {
    System.err.println("\n--- REFLECTION ATTACK FAILED ---");
    System.err.println("Exception: " + e.getClass().getSimpleName());
    System.err.println("Message: " + e.getMessage());
    }
    }
    }

    And we get this printout on the console:

    We get the exception caught in the catch and we do not see the printout from the 2nd enum instance because of the exception.

    Cheers!

    Unknown's avatar

    Author: Michael Clayton

    Technologist

    Leave a comment

    Design a site like this with WordPress.com
    Get started