You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 40 Next »

It is possible to reflectively access fields and methods of one object from another. Language access checks are enforced by the JVM to ensure policy compliance, while doing so. For instance, although an object is not normally allowed to access private members or invoke methods of another class, the APIs belonging to the java.lang.reflect package allow an object to do so contingent upon performing the language access checks.

The table below lists the APIs that should be used with care.

APIs that mirror language checks

java.lang.Class.newInstance

java.lang.reflect.Constructor.newInstance

java.lang.reflect.Field.get*

java.lang.reflect.Field.set*

java.lang.reflect.Method.invoke

java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater

java.util.concurrent.atomic.AtomicLongFieldUpdater.newUpdater

java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater

Note that the language access checks do not apply to java.lang.reflect.Field.setAccessible/getAccessible methods but to the remaining set* and get* field methods. The former APIs are protected by standard security manager checks.

Noncompliant Code Example

In this noncompliant code snippet, the package-private field i of class C can be accessed from class ReflectionExample. Method makeAccessible accepts fieldName as an input parameter which can be supplied by untrusted code. This is dangerous because despite the untrusted code not having the same capabilities as that of the immediate caller (method makeAccessible), it is allowed to carry out sensitive operations. In this case, the immediate caller has the capability of modifying package-private fields without triggering any language access checks. Hostile code should not be allowed to make such modifications by using it as an oracle.

// Class 'ReflectionExample' and 'C' belong to the same package
public class ReflectionExample {
  public static void makeAccessible(String fieldName) {
    C c = new C();
    try {
      Field f = c.getClass().getDeclaredField(fieldName);
      System.out.println(f.getInt(c)); // prints 10
      f.setInt(c, 1);  // set to 1; bypasses language access checks
      System.out.println(f.getInt(c)); // now prints 1
    }
    catch(NoSuchFieldException nsfa){}
    catch(IllegalAccessException iae) {}
  }
}

class C {
  int i = 10; // package-private
}

Compliant Solution

Do not operate on tainted inputs provided by untrusted code. Likewise, do not return values to an untrusted caller. If you must use Reflection, make sure that the immediate caller (method) is isolated from hostile code by declaring it final, reducing it's scope to private and making it non-static. Also, declare sensitive fields in other classes (Class c) as private.

private final void makeAccessible() { // private final
  String fieldName = "i"; // hardcode
  C c = new C();
  // ...
} 

class C {
  private int i = 10; // private
}

The permission ReflectPermission with action suppressAccessChecks should also not be granted so that the security manager blocks attempts to access private fields of other classes. (See ENV32-J. Do not grant ReflectPermission with target suppressAccessChecks)

Noncompliant Code Example

The class Trusted uses a package-private constructor in this noncompliant code example. It is desired that the code that exists outside the package be not allowed to create a new instance of an arbitrary class. However, since the API is public, it fails to achieve this condition. In this case, despite the package-private constructor, when an attacker passes Trusted.class as a parameter, the create() API returns an instance of the Trusted class.

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
  }
}

Compliant Solution

This compliant solution uses the java.beans.Beans API to explicitly specify the class loader that should be used to load the class obtained as the parameter. The attacker is unable to create an instance of the supplied class because the Beans.Instantiate() method has more stringent security checks that govern who is allowed reflective access and who is restricted.

package Safe;
import java.beans.Beans;

public class Trusted {
  Trusted() { }

  public static <T> T create(Class<T> c) {
    try {     
      ClassLoader cl = new SafeClassLoader();
      Object b = Beans.instantiate(cl, c.getName());
      return c.cast(b);
    } catch(Throwable t) { t.printStackTrace(); /* forward to handler */ }
    return null;
  }
}

// code outside the package
package Attacker;
import Safe.Trusted;

public class Attack {
  public static void main(String[] args) {
    Object o = Trusted.create(Trusted.class); // throws java.lang.IllegalAccessException, o = null
  }
}

Risk Assessment

Misuse of APIs that perform language access checks against the immediate caller only, can break data encapsulation.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

SEC03- J

high

probable

medium

P12

L1

Automated Detection

TODO

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

References

[[Chan 99]] java.lang.reflect AccessibleObject
[[SCG 07]] Guideline 6-4 Be aware of standard APIs that perform Java language access checks against the immediate caller


SEC02-J. Do not expose standard APIs that may bypass Security Manager checks to untrusted code      02. Platform Security (SEC)      SEC04-J. Do not rely on the default automatic signature verification provided by URLClassLoader and java.util.jar

  • No labels