6

语境

我正在编写一个 Java 系统,其中代码在非常严格的沙箱中执行。查询(由一个或多个类组成)在执行期间只允许访问一个文件夹(以及文件夹中包含的子文件夹和文件)。

我通过使用一个SecurityManager, 和一个新的ClassLoader每个查询执行来强制沙盒。在ClassLoaderusing中定义类时defineClass,我传递了一个ProtectionDomain包含应该授予的文件读取权限的文件。

由于并非调用堆栈上的所有对象都具有所需的权限,因此查询中的读取操作在AccessController.doPrivileged(...)-block 内运行。

问题

  • 当我AccessController.checkPermission(...)直接从doPrivileged(...)块内调用时,它会静默返回
  • 当我调用System.getSecurityManager().checkPermission(...)将请求转发到 时AccessController,会AccessController引发异常。
  • 通过?ProtectionDomain呼叫时似乎迷路AccessControllerSecurityManager
  • 受限制的文件操作(如创建java.io.FileReader),直接调用SecurityManager而不是AccessController. 当通过 调用时,如何让 尊重调用-block 的类的 ?AccessControllerSecurityManagerProtectionDomaindoRestricted(...)
  • 可能是它SecurityManager本身没有所需的权限吗?因此,通过被夹在特权代码之间的调用堆栈中,AccessController生成一个没有特权的联合?

下面是一个示例部分:

AccessController.doPrivileged(new PrivilegedAction<QueryResult>() {
  public QueryResult run() {
    String location = folderName + "/hello";
    FilePermission p = new FilePermission(location, "read");
    try {
      AccessController.checkPermission(p); // Doesn't raise an exception
      System.out.println("AccessController says OK");
      System.getSecurityManager().checkPermission(p);  // Raises AccessControlException
      System.out.println("SecurityManager says OK");
    } catch (AccessControlException e) {
      System.out.println("### Not allowed to read");
    }
    return null;
  }
});

程序生成的输出,包括部分堆栈跟踪(PATH 替换使用的长路径名):

AccessController says OK
Asked for permission: ("java.io.FilePermission" "PATH/hello" "read")
java.security.AccessControlException: access denied ("java.io.FilePermission" "PATH/hello" "read")
  at java.security.AccessControlContext.checkPermission(AccessControlContext.java:366)
  at java.security.AccessController.checkPermission(AccessController.java:560)
  at com.aircloak.cloak.security.CloakSecurityManager.checkPermission(CloakSecurityManager.java:40)
  at com.dummycorp.queries.ValidQuery$1.run(ValidQuery.java:23)
  at com.dummycorp.queries.ValidQuery$1.run(ValidQuery.java:1)
  at java.security.AccessController.doPrivileged(Native Method)
  at com.dummycorp.queries.ValidQuery.run(ValidQuery.java:16)
  at com.aircloak.cloak.security.CloakSecurityManagerTest$1.run(CloakSecurityManagerTest.java:75)
  at java.lang.Thread.run(Thread.java:722)

CloakAccessController.checkPermission(...)实施也可能很有趣。它看起来像这样:

public void checkPermission(Permission perm) {
  if (Thread.currentThread().getId() == this.masterThread) {
    return;
  } else {
    System.out.println("Asked for permission: "+perm.toString());
  }
  AccessController.checkPermission(perm);
}

它的作用主要是绕过创建它的线程的安全限制。


我的java.policy文件的内容是标准 MacOSX 系统的内容。我相信它不包含任何非标准的变化。

4

2 回答 2

5

回答我自己的问题我觉得有点尴尬,但我找到了正确的解决方案,并且认为在这里添加它是正确的,所以它被记录下来以备将来有人偶然发现这个问题。

TL;博士

我的自定义SecurityManager没有正确的权限。由于它位于调用 -block 的类doPrivileged(...)AccessController之间的调用堆栈上,因此权限的交集根本就没有权限。

长版

Java 安全模型的工作方式如下。当AccessController验证是否允许类调用方法时,它会从调用堆栈的顶部向底部查看权限。如果调用堆栈中的每个条目都具有权限,则允许该操作。

这是一个任意示例,一切正常:

+-----------+-------------------+-----------------------+
| Callstack | Class permissions | Permissions in effect |
+-----------+-------------------+-----------------------+
| Some      | {Read,Write}      | {Read}                |
| Other     | {Read}            | {Read}                |
+-----------+-------------------+-----------------------+

现在,就我的问题而言,调用堆栈中的较低层根本没有权限。因此,我们最终得到了这样的图片,其中query顶部的实际上没有权限。

+-----------+-------------------+-----------------------+
| Callstack | Class permissions | Permissions in effect |
+-----------+-------------------+-----------------------+
| Query     | {Read}            | {}                    |
| Other     | {}                | {}                    |
+-----------+-------------------+-----------------------+

您可以通过使用doPrivileged(...)-block 来解决此问题。这允许通过调用堆栈的权限搜索在调用特权操作的条目处结束:

+-----------+-------------------+-----------------------+
| Callstack | Class permissions | Permissions in effect |
+-----------+-------------------+-----------------------+
| Query     | {Read}            | {Read}                |
| Other     | {}                | {}                    |
+-----------+-------------------+-----------------------+

这就是为什么当我AccessController.checkPermission(...)在查询中调用 from 时一切正常的原因。毕竟它确实具有正确的权限。(不)幸运的是,java API(为了向后兼容)总是调用SecurityManager。就我而言,SecurityManager根本没有特权。因为它实际上是在进行特权调用的查询和AccessController之间的调用堆栈上,所以最终的权限是无的:

+-----------------+-------------------+-----------------------+
|    Callstack    | Class permissions | Permissions in effect |
+-----------------+-------------------+-----------------------+
| SecurityManager | {}                | {}                    |
| Query           | {Read}            | {Read}                |
| Other           | {}                | {}                    |
+-----------------+-------------------+-----------------------+

解决方案

解决方案是为SecurityManager提供一组基本权限。结果,授予 Query 的权限确实是所需的权限:

+-----------------+---------------------+-----------------------+
|    Callstack    |  Class permissions  | Permissions in effect |
+-----------------+---------------------+-----------------------+
| SecurityManager | {Read,Write,Delete} | {Read}                |
| Query           | {Read}              | {Read}                |
| Other           | {}                  | {}                    |
+-----------------+---------------------+-----------------------+

呸!那真是满口!希望这对那里的人有用:)

于 2012-12-12T08:59:19.553 回答
1

我认为这里的问题在于您如何ProtectionDomain提供SecurityManager.

如果你想自己加载类,并且能够使用 SM,你应该扩展SecureClassLoader。此类提供带有模板的方法:

protected Class defineClass(String name, byte[] b, int off, int len, CodeSource cs) 

您应该在其中为您的班级提供一个CodeSource。然后是另一种方法:

   protected  PermissionCollection getPermissions(CodeSource codesource) 

它将返回PermissionCollection给定CodeSource的类。这就是您应该为动态加载的类实现动态权限的方式。

于 2012-12-11T18:40:35.197 回答