在 Java 中,权限检查通常由 SecurityManager 处理。为了防止不受信任的代码调用特权代码并利用特权代码中的某些错误,SecurityManager 会检查整个调用堆栈;如果堆栈跟踪中的任何调用者没有特权,则默认情况下该请求被拒绝。至少,这就是标准 SecurityManager 检查的工作方式。
但是,一些特殊的 Java API 遵循不同的规则。他们绕过标准的 SecurityManager 检查,并替换为较弱的检查。特别是,它们只检查直接调用者,而不是整个调用堆栈。(有关详细信息,请参阅Java 安全编码指南的指南 9-8。特殊 API 包括,例如,、Class.forName()
等Class.getMethod()
。)
为什么?为什么这些特殊的 API 会绕过标准检查并替代较弱的检查?而且,为什么这是安全的?换句话说,为什么他们只检查直接调用者就足够了?这不是重新引入了标准 SecurityManager 检查旨在防御的所有风险吗?
我是在阅读最近的 Java 零日漏洞分析时才知道这一点的(CVE-2012-4681)。该分析解构了漏洞利用的工作原理。除其他外,攻击涉及利用这些特殊 API 进行的较弱检查。特别是,恶意 Java 代码设法获取对受信任系统类的引用(通过单独的错误),然后欺骗受信任系统类调用这些特殊 API 之一。生成的权限检查仅查看其直接调用者,看到直接调用者是受信任的,并允许该操作——即使该操作最初是由不受信任的代码发起的。因此,较弱的检查不会阻止攻击,但据我所知,使用标准 SecurityManager 检查可以防止这种攻击(因为调用者的调用者不受信任)。换句话说,
但是,我知道 Java 设计者是聪明人。我怀疑 Java 设计者一定已经考虑过这些问题,并且有充分的理由绕过标准检查并用较弱的检查代替这些特殊的 API——或者,至少,他们认为他们有充分的理由证明这是安全的。所以,也许我错过了一些东西。
任何人都可以对此有所了解吗?Java 设计者是否搞砸了这些特殊的 API,或者是否有正当的理由来替换较弱的检查?
编辑 9/1:我不是在问这个漏洞是如何工作的;我想我理解这个漏洞是如何工作的。我也没有问为什么在这个特定的例子中,调用这些特殊 API 的可信代码有问题。相反,我在问为什么特殊的 API——比如Class.forName()
,Class.getMethod()
等等——被指定并实现为使用非标准的较弱权限检查(只看直接调用者)而不是标准的 SecurityManager 权限检查(看在整个调用堆栈)。这个设计决策(对那些特殊的 API 使用较弱的权限检查)允许最近的漏洞,因此很容易批评设计决策。但是,我想这样做可能有一些很好的理由,我想知道这些可能是什么。