...
Code Block | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
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.
...