Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: replaced last CS, please review

...

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

In the presence of a security manager, the Class.newInstance() method throws a security exception when either:

  • invocation of s.checkMemberAccess(this, Member.PUBLIC) denies creation of new instances of this class
  • the caller's class loader is not the same as or an ancestor of the class loader for the current class and invocation of s.checkPackageAccess() denies access to the package of this class

For this first condition, "The default policy is to allow access to PUBLIC members, as well as access to classes that have the same class loader as the caller.". This may be unsafe, as was demonstrated in this noncompliant code example.

Compliant Solution

This compliant solution uses checks whether the Class object has any public constructors. If it does, the java.beans.Beans API is used to explicitly specify the class loader that should be used to load instantiate 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 restrictedobject. If no public constructors are present, the security manager's checkPackageAccess() method is invoked to ensure that the caller has sufficient permissions to access members of the package Safe.

Code Block
bgColor#ccccff
package Safe;
import java.beans.Beans;
import java.io.IOException;

public class Trusted  {
  Trusted() { }
  
  public static <T> T create(Class<T> c) throws  IOException, ClassNotFoundException, 
    InstantiationException, IllegalAccessException {
    
    if(c.getConstructors().length == try0) {  // No public constructors  	  
      ClassLoaderSecurityManager clsm = new SafeClassLoaderSystem.getSecurityManager();    
      Objectif b(sm = Beans.instantiate(cl, c.getName());!= null) {          
        sm.checkPackageAccess("Safe");          
      }
      return c.castnewInstance(b); // Safe to return 
    } catch(Throwable t) { t.printStackTrace(); /* forward to handler */ } 

    // Executes only if there are public constructors
    ClassLoader cl = new SafeClassLoader();
    Object b = Beans.instantiate(cl, c.getName());
    return null;c.cast(b);      
  }  
}

// 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.

...