May 29, 2017

How to exploit Singleton class with Reflection API and Its prevention

Singleton design pattern is intended to create only one instance of given class in context of given class loader (class loader which loads this given class). i.e: One instance per class loader. This post will discuss how to exploit a Singleton class with Reflection API and create multiple instance of that class followed by how do we resolve it.

Below is a singleton class with instance created while class is loaded in JVM by a class loader.
class SingletonBreakable {

 private static final SingletonBreakable INSTANCE = new SingletonBreakable();

 private SingletonBreakable() {
  // No object creation allowed from outside world
 }

 public static SingletonBreakable getInstance() {
  return INSTANCE;
 }

 public Object clone() throws CloneNotSupportedException {
  throw new CloneNotSupportedException(
    "Cannot clone instance of this class");
 }

 public void display() {
  System.out.println("Hello from SingletonBreakable");
 }
}

In order to prevent multiple object creation, above class provides private constructor and it looks logical. But Java provides reflection API with we can create Multiple instance of this class and it breaks Singleton goal of creating only one instance of given class.

Lets use reflection API and try to create multiple object. Below sample class makes private constructor accessible using Reflection API and create another object from outside (other than provided by Singleton class SingletonBreakable)
package javacore;

import java.lang.reflect.Constructor;

public class SingletonBreakableClinet {

 /**
  * @param args
  */
 public static void main(String[] args) {
  /*
   * 1.Execute display method using default object provided by
   * SingletonBreakable and display hashCode too.
   */
  SingletonBreakable sb1 = SingletonBreakable.getInstance();
  System.out.println("Hashcode of original object is  " + sb1.hashCode());
  sb1.display();

  /*
   * 2. Use reflection to create duplicateObject and display its hashCode.
   */
  SingletonBreakable sb2 = createObjetUsingRefletion();
  System.out.println("Hashcode of Duplicate Object is  " + sb2.hashCode());
  sb2.display();
 }

 public static SingletonBreakable createObjetUsingRefletion() {
  SingletonBreakable sb = null;
  try {
   Constructor<SingletonBreakable> constructor = SingletonBreakable.class
     .getDeclaredConstructor(new Class[0]);
   constructor.setAccessible(true);
   sb = constructor.newInstance();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return sb;
 }
}

Sample Output:-

Hashcode of original object is 576315909
Hello from SingletonBreakable
Hashcode of Duplicate Object is  990234593
Hello from SingletonBreakable

Hashcode of both object is different, it indicates we are able to create multiple object using reflection. It breaks Singleton contract and claim to create Just one instance of this class with respect of one class loader.

How to prevent reflection to create multiple instance of Singleton ? 

Instead of creating a empty private constructor, we need to throw exception from it, if instance is already created (which will be always created as class is loaded). So modified class is:

class SingletonThrowException {

 private static final SingletonThrowException INSTANCE = 
   new SingletonThrowException();

 private SingletonThrowException() {
  if (INSTANCE != null) {
   throw new IllegalStateException("Already instantiated");
  }
 }

 public static SingletonThrowException getInstance() {
  return INSTANCE;
 }

 public Object clone() throws CloneNotSupportedException {
  throw new CloneNotSupportedException(
    "Cannot clone instance of this class");
 }

 public void display() {
  System.out.println("Hello from SingletonThrowException "); }
}

 We have added a check in above constructor, if we will try to create instance of this class, exception will be thrown and object creation will not be successful. Now we try to create duplicate object and validate the modification.


package javacore;

import java.lang.reflect.Constructor;

public class SingletonUnbreakableClinet {
 public static void main(String[] args) {
  /*
   * 1.Execute display method using default object provided by
   * SingletonBreakable and display hashCode too.
   */
  SingletonThrowException sb1 = SingletonThrowException.getInstance();
  System.out.println("Hashcode of original object is  " + sb1.hashCode());
  sb1.display();

  /*
   * 2. Use reflection to create duplicateObject and display its hashCode.
   */
  SingletonThrowException sb2 = createObjetUsingRefletion();
  if (sb2 != null)
   System.out.println("Hashcode of Duplicate Object is  "
     + sb2.hashCode());
  else
   System.out.println("Object creation failed !!");
 }

 public static SingletonThrowException createObjetUsingRefletion() {
  SingletonThrowException sb = null;
  try {
   Constructor<SingletonThrowException> constructor = 
     SingletonThrowException.class
     .getDeclaredConstructor(new Class[0]);
   constructor.setAccessible(true);
   sb = constructor.newInstance();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return sb;
 }
}

Sample Output:-

Hashcode of original object is  1486987673
Hello from SingletonThrowException

java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at javacore.SingletonUnbreakableClinet.createObjetUsingRefletion(SingletonUnbreakableClinet.java:32)
at javacore.SingletonUnbreakableClinet.main(SingletonUnbreakableClinet.java:18)
Caused by: java.lang.IllegalStateException: Already instantiated
at javacore.SingletonThrowException.<init>(SingletonThrow.java:56)
... 6 more
Object creation failed !!

Here only one instance of object is created and  when reflection API tries to create an instance it fails. so in this way we can prevent Object creation using reflection API. 

Summary :- "Always throws Exception from private constructor in the case of Singleton class."

Location: Hyderabad, Telangana, India