In Java, normally permission checks are handled by the SecurityManager. To prevent untrusted code from invoking privileged code and exploiting some bug in the privileged code, SecurityManager checks the entire call stack; if any of the callers in the stack trace are unprivileged, by default the request is denied. At least, that's how the standard SecurityManager checks work.
However, a few special Java APIs follow different rules. They bypass the standard SecurityManager checks, and substitute a weaker check. In particular, they check only the immediate caller, not the entire call stack. (See Guideline 9-8 of the Java Secure Coding Guidelines for details. The special APIs include, for example, Class.forName()
, Class.getMethod()
, and more.)
Why? Why do these special APIs bypass the standard checks and substitute a weaker check? And, why is this safe? In other words, why is it sufficient for them to check only the immediate caller? Doesn't this re-introduce all of the risks that the standard SecurityManager checks were designed to defend against?
I first learned of this when reading an analysis of the recent Java zero-day exploit (CVE-2012-4681). That analysis deconstructs how the exploit works. Among other things, the attack involves taking advantage of the weaker checking done by these special APIs. In particular, the malicious Java code manages to get a reference to a trusted system class (through a separate bug), then it fools that trusted system class into invoking one of these special APIs. The resulting permission check looks only at its immediate caller, sees that the immediate caller is trusted, and allows the operation -- even though the operation was originally initiated by untrusted code. Thus the weaker checks don't stop the attack, but as far as I can see, this attack would have been prevented by using the standard SecurityManager checks (since the caller's caller is untrusted). In other words, this recent attack looks like an example of why the weaker check is risky.
However, I know the Java designers are smart folks. I suspect the Java designers must have considered these issues and had some good reason to bypass the standard checks and substitute the weaker checks for these special APIs -- or, at least, thought they had a good reason why this was safe. So, maybe I'm missing something.
Can anyone shed any light on this? Did the Java designers screw up with these special APIs, or were there valid reasons for substituting the weaker checks?
Edit 9/1: I'm not asking about how the exploit works; I think I understand how the exploit works. I'm also not asking why, in this particular example, the trusted code that invoked these special APIs was buggy. Rather, I'm asking why the special APIs -- like Class.forName()
, Class.getMethod()
, and so on -- are specified and implemented to use the non-standard weaker permission check (only look at the immediate caller) instead of the standard SecurityManager permission check (look at the entire call stack). This design decision (of using weaker permission checks for those special APIs) allowed the recent vulnerability, so it would be easy to criticize the design decision. However, I imagine there might have been some good reasons for doing things this way, and I'm wondering what those might be.