Java为创建的实例设置安全权限

时间:2016-03-18 18:01:45

标签: java permissions securitymanager

所以我有一些代码,它创建了一个类的实例。

Class<?> c = Class.forName("MyClass");
Constructor<?> cons = c.getConstructor();
cons.setAccessible(true);
Object instance = cons.newInstance();

现在我想为该实例设置一些限制。我打电话的时候:

instance.doSomething();

我想为(位实例的)代码设置限制。因此,从isntance调用的方法不能做一些可疑的事情(系统调用,文件操作......)。

我曾尝试设置安全管理器,但是这会限制所有代码(我仍然想要读取/写入其余代码的文件)。 是否可以仅限制某些对象?

1 个答案:

答案 0 :(得分:2)

TL; DR:Code

<小时/> 问题基本上是&#34;如何在特定实例上调用方法,权限低于正常?&#34; 。这里有三个要求:

  1. 代码将按实例授权。默认情况下,实例具有特权。
  2. 实例可能会被选择性地列入黑名单,也就是说,它可能被赋予比通常情况下更低的权限,它接收的方法调用的持续时间
  3. 黑名单必须传播到代表接收者执行的代码,特别是与其交互的相同类型的任何对象,包括其自身;否则,如果接收者轮流打电话

    AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
        this.doSomethingElse();
        return null;
    });
    

    doSomethingElse()会逃离沙箱。

  4. 这三个都有问题:

    • 第一个实际上并不是 1 ,因为它预先假定运行时维护 - 并公开 - 关于线程上的实例(而不仅仅是类)的信息#39;执行堆栈,它不是 2
    • 第二个和第三个只有在任何列入黑名单的代码通过AccessController.doPrivileged(...)声明其自己的(默认的,基于类的)特权时才可以实现,这可能是设计的随时选择。

    还有替代方案吗?
    那么,你愿意走多远?修改AccessController / AccessControlContext内幕?或者更糟糕的是,VM的内部?或者也许提供自己的SecurityManager重新实现上述组件&#39;功能从零开始,以满足您的要求的方式?如果对所有人的回答是&#34; no&#34;,那么我担心你的选择是有限的。

    顺便说一下,理想情况下,当你被问到时,你应该能够做出二元选择吗?这个特定的代码,即,能否或者不能被赋予特定的特权? &#34; ,因为这将极大地简化 3 事物。不幸的是你不能;而且,更糟糕的是,你可能也不可能修改班级的实施,以便所有的实例都可以考虑 - 关于特定的一组特权 - 值得信赖,也不希望简单地标记这个类,因此它的所有实例都是不可信的(我相信你应该这样做!)并与它一起生活。

    继续讨论建议的解决方法。为了克服前面列出的缺点,原始问题将被重新描述如下:&#34;如何使用提升特权来调用方法接收器&#39; s ProtectionDomain ?&#34; 我将回答这个衍生问题,建议与原始问题相反:

    1. 代码应由其类ProtectionDomain授权,通常情况下如此。默认情况下,代码是沙箱。
    2. 在特定线程下的方法调用期间,代码可以选择性地列入白名单。
    3. 白名单必须将 4 传播到接收方调用的同一类的代码。
    4. 修订后的要求将通过自定义ClassLoaderDomainCombiner来满足。第一个目的是为每个类 5 分配一个不同的ProtectionDomain;另一个是暂时替换当前AccessControlContext内各个类的域名,以便按需白名单&#34;目的。另外扩展SecurityManager以防止非特权代码 4 创建线程。

      <小时/> 注意:我将代码重新定位到this gist,以使帖子的长度低于限制 标准免责声明:概念验证代码 - 需要几汤匙盐!

      运行示例
      根据示例策略配置文件的建议编译和部署代码,即应该有两个 6 不相关的类路径条目(例如,兄弟目录在文件系统级别) - 一个用于com.example.trusted包的类,另一个用于com.example.untrusted.Nasty

      确保您已将策略配置替换为示例配置,并已根据需要修改其中的路径。

      最后运行(当然,在适当修改了类路径条目之后):

      java -cp /path/to/classpath-entry-for-trusted-code:/path/to/classpath-entry-for-untrusted-code -Djava.system.class.loader=com.example.trusted.DiscriminatingClasspathClassLoader com.example.trusted.Main
      

      第一次调用不受信任的方法应该会成功,第二次调用失败。

      <小时/> 1 特制类的实例(例如,由某个可信组件分配的,具有自己的域)的实例可能会行使自己的权限自己< / em>(在这种情况下不成立,因为你无法控制instance类的实现,它出现了)。然而,这仍然不能满足第二和第三个要求。
      2 回想一下,在默认SecurityManager下,当所有Permission s-通常是类而不是实例时,都会授予ProtectionDomain映射了该权限的AccessControlContext imply线程。
      3 如果您认为该类值得信任,或者根本不授予任何内容,则您只需在策略级别授予权限,而不必担心每个安全上下文的每个实例的权限什么。
      4 这是一个艰难的决定:如果白名单不会影响相同类型的后续被调用者,则该实例将无法在其自身上调用任何需要权限的方法 。另一方面,它确实与原始列入白名单的方法接收器进行交互的同一类型的任何其他实例一样,也具有特权!因此,您必须确保接收方呼叫任何&#34;不信任的&#34;它本身的实例。出于同样的原因,允许接收器产生任何线程是一个坏主意。
      5 与默认应用程序ClassLoader使用的策略相反,默认应用程序ProtectionDomain用于将位于同一类路径条目下的所有类分组到一个ProtectionDomain内。< /子>
      6 造成不便的原因是我们的自定义应用程序ClassLoader的类被其父级映射到的CodeSource具有CodeSource {1}}暗示所有JAVA_HOME引用加载程序的类路径条目下的文件。到现在为止还挺好。现在,当被要求加载一个类时,我们的加载器会尝试通过测试.class文件是否位于JAVA_HOME之下来分辨系统/扩展类(它委托给它的父级)和应用程序类。当然,要允许这样做,它需要对SubjectDomainCombiner下的文件系统子树进行读取访问。不幸的是,向我们的加载器(众所周知的广泛)域授予相应的权限,隐式地将权限授予驻留在加载器的类路径条目下的所有其他类的域,包括不受信任的域。这应该有希望解释为什么必须在可信代码和不可信代码之间进行类路径入口级隔离。当然,一如既往地有解决方法;例如强制要求对可信代码进行额外签名以获得任何特权;或者使用更灵活的URL方案进行代码源识别,和/或改变代码源隐含语义。 进一步阅读:

      <小时/> 历史记录:最初这个答案提出了一个几乎相同的解决方案,滥用依赖于JAAS的Principal,而不是自定义的,用于动态权限修改。 A&#34;特别&#34; Permission会附加到特定域名,然后Policy会根据其CodeSource - Principal个身份对{{1}}进行评估,从而产生额外的{{1}}。