Java设置创建实例的安全权限 [英] Java set security permission of created instances

查看:106
本文介绍了Java设置创建实例的安全权限的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我有一些代码,可以创建一个类的实例.

So I have a bit of code, that creates an instance of a class.

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

现在,我想对该实例设置一些限制.当我打电话时:

Now I want to set some restrictions to that instance. When I call:

instance.doSomething();

我想为(实例的)那段代码设置限制.因此,从那个地方调用的方法不能做任何令人讨厌的事情(系统调用,文件操作...).

I want to set restrictions for that bit of code (of the instance). So the methods called from that isntance can not do something fishy (System calls, File operations...).

我试图设置一个安全管理器,但这限制了所有代码(我仍然想为我的其余代码读取/写入文件). 是否可以仅限制某些对象?

I have tried to set a security manager, but that restricts all of the code (I still want to read/write files for the rest of my code). Is it possible to restrict only certain objects?

推荐答案

TL; DR:代码


问题本质上是如何在特权低于普通权限的特定实例上调用方法?" .这里有三个要求:

TL;DR: Code


The question is essentially "How do I invoke a method on a particular instance, with privileges lower than normal?". There are three requirements here:

  1. 代码应按实例进行授权.默认情况下,实例具有特权.
  2. 实例可能会被有选择地列入黑名单,即,在实例接收到的方法调用持续时间内,该实例可能会获得比通常更低的特权.
  3. 黑名单必须传播到代表接收者执行的代码,尤其是与之交互的相同类型的任何对象,本身包括在内;否则,例如,如果接收者依次打电话给

  1. Code is to be authorized on a per-instance basis. An instance is privileged by default.
  2. An instance may be selectively blacklisted, i.e., it may be accorded lower privileges than it normally would have been, for the duration of a method invocation that it receives.
  3. Blacklisting must propagate to code executed on the receiver's behalf, specifically any objects of the same type that it interacts with, itself included; otherwise, if, say, the receiver were in turn to call

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

doSomethingElse()会逃脱沙箱.

这三个都有问题:

  • 第一个不是真的 1 是可实现的,因为它假定运行时维护有关线程上的实例(而不仅仅是类)的信息(并公开) '执行堆栈,它不会 2 .
  • 仅当任何被列入黑名单的代码通过AccessController.doPrivileged(...)声明自己的(默认的,基于类的)特权时,才可以实现第二个和第三个特权.时间选择.
  • The first one is not really1 achievable, because it presupposes that the runtime maintain—and expose—information about the instances (rather than merely the classes) on threads' execution stacks, which it does not2.
  • The second and third are only achievable as long as any blacklisted code does not assert its own (default, class-based) privileges via AccessController.doPrivileged(...), which, by design, it may at any time choose to.

还有其他选择吗?
好吧,你愿意走多远?修改AccessController/AccessControlContext内部?或更糟糕的是,VM的内部?还是提供您自己的SecurityManager来以满足您要求的方式从头开始重新实现上述组件的功能?如果所有人的答案都是否",那么我担心您的选择会受到限制.

Is there an alternative?
Well, how far are you willing to go? Modify AccessController / AccessControlContext internals? Or worse yet, internals of the VM? Or perhaps provide your own SecurityManager that reimplements the aforementioned components' functionality from scratch, in a way that satisfies your requirements? If the answer to all is "no", then I fear that your options are limited.

顺便说一句,理想情况下,当被问及是否可以将特定特权(即 class "委托给该特定代码?)时,您应该能够做出二进制选择?" ,因为这将大大简化 3 的过程.不幸的是你不能;而且,更糟的是,您不能修改类的实现,以致于它的所有实例(就特定的特权集而言)都可以被认为是不可信的,或者您不想简单地标记该类,并因此将其所有实例视为不受信任的对象(我相信您应该这样做!)并与之一起生活.

As an aside, you should ideally be able to make a binary choice when asked "Can or cannot this particular code, i.e. class, be entrusted with the particular privileges?", for this would tremendously simplify3 things. Unfortunately you cannot; and, to make matters worse, neither can you, presumably, modify the implementation of the class such that all of its instances can either be considered—with regards to a specific set of privileges—trustworthy or not, nor do you wish to simply mark the class, and therefore all of its instances, as untrusted (which I do believe you should!) and live with it.

继续进行建议的解决方法.为了克服前面列出的缺点,原始问题将表述为:"我如何调用具有方法接收者的ProtectionDomain 权限的 elevated 特权的方法>?" 我将回答这个派生问题,与原始问题相反,建议:

Moving on to the proposed workaround. To overcome the shortcomings listed earlier, the original question will be rephrased as follows: "How do I invoke a method with elevated privileges accorded to the method receiver's ProtectionDomain?" I am going to answer this derivative question instead, suggesting, in contrast to the original one, that:

    通常,
  1. 代码应由其类的ProtectionDomain授权.默认情况下,代码是沙盒化的.
  2. 在特定线程下的方法调用期间,可以选择将代码列入白名单.
  3. 白名单必须传播 4 到接收方调用的同一类代码.
  1. Code is to be authorized by the ProtectionDomain of its class, as is normally the case. Code is sandboxed by default.
  2. Code may be selectively whitelisted, for the duration of a method invocation under a particular thread.
  3. Whitelisting must propagate4 to code of the same class called by the receiver.

修订后的要求将通过自定义ClassLoaderDomainCombiner来满足.第一个目的是为每个类 5 分配一个不同的ProtectionDomain;另一个是临时替换当前AccessControlContext中各个类的域,以实现按需白名单"目的. SecurityManager进行了额外扩展,以防止非特权代码 4 创建线程.


注意:我将代码重新定位到 此要点 以保留帖子的内容长度小于限制.
标准免责声明:概念验证代码-与几汤匙盐一起服用!

The revised requirements will be satisfied by means of a custom ClassLoader and DomainCombiner. The purpose of the first is to assign a distinct ProtectionDomain per class5; the other's is to temporarily replace the domains of individual classes within the current AccessControlContext for "on-demand whitelisting" purposes. The SecurityManager is additionally extended to prevent thread creation by unprivileged code4.


Note: I relocated the code to this gist to keep the post's length below the limit.
Standard disclaimer: Proof-of-concept code—take with several tablespoons of salt!

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

Running the example
Compile and deploy the code as suggested by the example policy configuration file, i.e., there should be two6 unrelated classpath entries (e.g. sibling directories at the filesystem level)—one for classes of the com.example.trusted package, and another for com.example.untrusted.Nasty.

还要确保已用示例一替换了策略配置,并已适当修改了其中的路径.

Ensure also that you have replaced the policy configuration with the example one, and have modified the paths therein as appropriate.

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

Lastly run (after having appropriately modified the classpath entries, of course):

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

The first call to the untrusted method should hopefully succeed, and the second fail.


1 It would perhaps be possible for instances of a specially crafted class (having, e.g., a domain of their own, assigned by some trusted component) to exercise their own privileges themselves (which does not hold true in this case, since you have no control over the implementation of instance's class, it appears). Nevertheless, this would still not satisfy the second and third requirement.
2 Recall that, under the default SecurityManager, a Permission is granted when all ProtectionDomains—to which normally classes, rather than instances, are mapped—of the thread's AccessControlContext imply that permission.
3 You would then simply have to grant permissions at the policy level, if you deemed the class trustworthy, or otherwise grant nothing at all, rather than have to worry about permissions per instance per security context and whatnot.
4 This is a hard decision: If whitelisting did not affect subsequent callees of the same type, the instance would not be able to call any privilege-requiring methods on itself. Now that it does, on the other hand, any other instance of the same type, that the original whitelisted method receiver interacts with, become privileged too! Thus you must ensure that the receiver does not call any "untrusted" instances of its own kind. It is for the same reason a bad idea to allow the receiver to spawn any threads.
5 As opposed to the strategy employed by the default application ClassLoader, which is to group all classes that reside under the same classpath entry within a single ProtectionDomain.
6 The reason for the inconvenience is that the ProtectionDomain, which our custom application ClassLoader's class gets mapped to by its parent, has a CodeSource implying all CodeSources referring to files under the loader's classpath entry. So far so good. Now, when asked to load a class, our loader attempts to discern between system/extension classes (loading of which it delegates to its parent) and application classes, by testing whether the .class file is located below JAVA_HOME. Naturally, to be allowed to do so, it requires read access to the filesystem subtree beneath JAVA_HOME. Unfortunately, granting the corresponding privilege to our loader's (notoriously broad) domain, implicitly grants the privilege to the domains of all other classes residing beneath the loader's classpath entry, including untrusted ones, as well. And that should hopefully explain why classpath entry-level isolation between trusted and untrusted code is necessary. There are of course workarounds, as always; e.g. mandating that trusted code be additionally signed in order to accrue any privileges; or perhaps using a more flexible URL scheme for code source identification, and/or altering code source implication semantics.
Further reading:

  • Default Policy Implementation and Policy File Syntax
  • API for Privileged Blocks
  • Secure Coding Guidelines for Java SE - §9 - Access Control
  • Troubleshooting Security


历史记录:最初,此答案提出了一个几乎相同的解决方案,该解决方案依赖于JAAS的SubjectDomainCombiner而不是自定义的,用于动态特权修改. 特殊" Principal将附加到特定域,然后根据CodeSource-Principal的综合身份,由Policy评估后,会产生另外的Permission.


Historical note: Originally this answer proposed a nearly identical solution that abusedrelied on JAAS's SubjectDomainCombiner, rather than a custom one, for dynamic privilege modification. A "special" Principal would be attached to specific domains, which would then accrue additional Permissions upon evaluation by the Policy, based on their composite CodeSource-Principal identity.

这篇关于Java设置创建实例的安全权限的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆