Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Added related vul

Reflection enables a Java program to analyze and modify itself. In particular, a program can discover the values of field variables and change them [Forman 05], [Sun 02]. The Java reflection API includes a method that enables fields that are normally inaccessible to be accessed under reflection. The following code prints out the names and values of all fields of an object someObject of class SomeClass:

Code Block

Field fields[] = SomeClass.class.getDeclaredFields();
for (Field field : fields) {
  if ( !Modifier.isPublic(field.getModifiers())) {
    field.setAccessible(true);
  }
  System.out.print("Field: " + field.getName());
  System.out.println(", value: " + field.get(someObject));
}

A field could be set to a new value as follows:

Code Block

String newValue = reader.readLine();
field.set(someObject, returnValue(newValue, field.getType()));

...

  • leak information about field names by throwing an exception for invalid or inaccessible field names. See rule ERR01-J. Do not allow exceptions to expose sensitive information for additional information. This example complies with rule ERR01-J by catching the relevant exceptions at the end of the method.
  • access potentially sensitive data that is visible to zeroField() but is hidden from the attacking method. This privilege escalation attack can be difficult to find during code review because the specific field(s) being accessed are controlled by strings in the attacker's code rather than by locally visible source code.
Code Block
bgColor#FFcccc

class FieldExample {
  private int i = 3;
  private int j = 4;

  public String toString() {
    return "FieldExample: i=" + i + ", j=" + j;
  }

  public void zeroI() {
    this.i = 0;
  }

  public void zeroField(String fieldName) {
    try {
      Field f = this.getClass().getDeclaredField(fieldName);
      // Subsequent access to field f passes language access checks
      // because zeroField() could have accessed the field via
      // ordinary field references
      f.setInt(this, 0);
      // log appropriately or throw sanitized exception; see EXC06-J
    } catch (NoSuchFieldException ex) {
      // report to handler
    } catch (IllegalAccessException ex) {
      // report to handler
    }
  }

  public static void main(String[] args) {
    FieldExample fe = new FieldExample();
    System.out.println(fe.toString());
    for (String arg : args) {
      fe.zeroField(arg);
      System.out.println(fe.toString());
    }
  }
}

...

When you must use reflection, make sure that the immediate caller (method) is isolated from hostile code by declaring it private or final, as in this compliant solution.

Code Block
bgColor#ccccff

class FieldExample {
  // ...

  private void zeroField(String fieldName) {
    // ...
  }
}

...

When a class must use reflection to provide access to fields, it must also provide the same access using a nonreflection interface. This compliant solution provides limited setter methods that grant all callers the ability to zero out its fields without using reflection. If these setter methods comply with all other rules or security policies, the use of reflection also complies with this rule.

Code Block
bgColor#ccccff

class FieldExample {
  // ...

  public void zeroField(String fieldName) {
    // ...
  }

  public void zeroI() {
    this.i = 0;
  }
  public void zeroJ() {
    this.i = 0;
  }
}

...

In this noncompliant code example, the programmer intends that code outside the Safe package should be prevented from creating a new instance of an arbitrary class. Consequently, the Trusted class uses a package-private constructor. However, because the API is public, an attacker can pass Trusted.class itself as an argument to the create() method and bypass the language access checks that prevent code outside the package from invoking the package-private constructor. The create() method returns an unauthorized instance of the Trusted class.

Code Block
bgColor#FFcccc

package Safe;
public class Trusted {
  Trusted() { } // package private constructor
  public static <T> T create(Class<T> c)
      throws InstantiationException, IllegalAccessException {
    return c.newInstance();
  }
}

package Attacker;
import Safe.Trusted;

public class Attack {
  public static void main(String[] args)
      throws InstantiationException, IllegalAccessException {
    System.out.println(Trusted.create(Trusted.class)); // succeeds
  }
}

...

This compliant solution reduces the access of the create() method to package-private, preventing a caller from outside the package from using that method to bypass the language access checks to create an instance of the Trusted class. Any caller that can create a Trusted class instance using reflection can simply call the Trusted() constructor instead.

Code Block
bgColor#ccccff

package Safe;
public class Trusted {
  Trusted() { } // package private constructor
  static <T> T create(Class<T> c)
      throws InstantiationException, IllegalAccessException {
    return c.newInstance();
  }
}

...

This compliant solution uses the getConstructors() method to check whether the class provided as an argument has public constructors. The security issue is irrelevant when public constructors are present because such constructors are already accessible even to malicious code. When public constructors are absent, the create() method uses the security manager's checkPackageAccess() method to ensure that all callers in the execution chain have sufficient permissions to access classes and their respective members defined in package Safe.

Code Block
bgColor#ccccff

import java.beans.Beans;
import java.io.IOException;
package Safe;

public class Trusted  {
  Trusted() { }
  
  public static <T> T create(Class<T> c)
      throws InstantiationException, IllegalAccessException {
    
    if (c.getConstructors().length == 0) {  // No public constructors 
      SecurityManager sm = System.getSecurityManager();    
      if (sm != null) {
        // throws an exception when access is not allowed          
        sm.checkPackageAccess("Safe");          
      }
    } 
    return c.newInstance(); // Safe to return     
  }  
}

...

This compliant solution uses the java.beans.Beans API to check whether the Class object being received has any public constructors.

Code Block
bgColor#ccccff

public class Trusted {
  Trusted() { }
  
  public static <T> T create(Class<T> c)
      throws IOException, ClassNotFoundException {
    
    // Executes without exception only if there are public constructors
    ClassLoader cl = new SafeClassLoader();
    Object b = Beans.instantiate(cl, c.getName());
    return c.cast(b);      
  }  
}

The Beans.instantiate() method succeeds only when the class being instantiated has a public constructor; otherwise, it throws an IllegalAccessException. The method uses a class loader argument along with the name of the class to instantiate. Unlike the previous compliant solution, this approach avoids the need for any reflection permissions.

Risk Assessment

Related Vulnerabilities

CERT Vulnerability #636312 describes an exploit in Java that allows malicious code to disable any security manager currently in effect. Among other vulnerabilities, the attack code exploited the following method defined in sun.awt.SunToolkit, for Java 7:

Code Block
bgColor#ccffff
public static Field getField(final Class klass, final String fieldName) {
  return AccessController.doPrivileged(new PrivilegedAction<Field>() {
       public Field run() {
           try {
               Field field = klass.getDeclaredField(fieldName);
               assert (field != null);
               field.setAccessible(true);
               return field;
           } catch (SecurityException e) {
               assert false;
           } catch (NoSuchFieldException e) {
               assert false;
           }
           return null;
       }//run
  });
}

This code operates inside a doPrivileged() block. It then uses the reflection method Class.getDeclaredField() to obtain a field given the field's class and name. This method would normally be blocked by a security manager. It then uses the reflection method Field.setAccessible() to make the field accessible, even if it were protected or private. But this method is public, so anyone can call it.

Risk Assessment

Misuse of APIs that perform language access checks only against the immediate caller can break data encapsulation, Misuse of APIs that perform language access checks only against the immediate caller can break data encapsulation, leak sensitive information, or permit privilege escalation attacks.

...

[Chan 1999]

java.lang.reflect AccessibleObject

 

      14. Platform Security (SEC)