如何防止仅对某些类进行反射访问

时间:2017-09-25 13:12:52

标签: java security

我正在开发一些许可代码,因此我希望避免通过反射访问一个或两个类。

我尝试过实现一个SecurityManager但没有成功。

我发现另一种选择是定义我自己的策略文件,但是这需要我在JVM启动时设置它,这违背了我的目的,我希望能够设置一个防止访问特定的SecurityManager许可证类,或在运行时执行此操作的策略。

1 个答案:

答案 0 :(得分:1)

Notes&免责声明

  • 答案假定您控制,或以其他方式信任环境;除非是这种情况,否则应用它将是完全无效的。
  • 答案与Java SE < = 8 相关。从9开始,提供的代码不太可能按预期工作,并且模块系统减少了实际需要使用安全框架以实现隔离的情况。
  • 代码是(正如互联网上的大多数内容)提供的"原样"。

<强>先决条件

确保任何不受信任的代码与受信任的代码隔离。在此上下文中的隔离意味着:

  • ProtectionDomain s之间的分离(两个不同级别的两个代码库的两个域&#34;可信度&#34;应该具有不相等的,非暗示的CodeSource s),
  • packages,
  • ClassLoader秒。

虽然第一个通常是足够的,但在授权和可访问性问题之间的界限模糊的情况下,如反射的情况,后两点也是必要的。我将留下解决此问题的详细信息。因为我们在这里,有一个关于术语的说明:既然JDK没有一致地根据上述属性对客户端代码进行授权,我只是简单地指代运行任何一个的代码。三个授权决策时作为隔离单元

选项A - 限制包访问

也许最简单的解决方案是防止不受信任的代码访问特定的包:

  • 拥有敏感代码的通用(基础)包。
  • 通过package.access中的<JAVA_HOME>/lib/security/java.security属性或通过java.security.Security#setProperty将程序包名称附加到相应的安全属性,以编程方式将其声明为受限制。
  • 让客户通过位于受限软件包之外的您的可信中介间接访问您的敏感代码。该课程应授予RuntimePermission "accessClassInPackage." + restrictedPackageName

但是有一个问题 - 你也希望不受信任的代码能够完全反射访问宇宙的其他部分&#34;,这绝不可能在没有隐含地授予它访问带来沙箱跪在地上,如果它如此喜欢,例如,重新分配System' s SecurityManager字段。这导致我们......

选项B - 有选择地拒绝反射访问

<强>目标

  • 基于以下属性授予或拒绝基于每个操作的反射访问:
    • 反映成员的类型(字段,方法等)。
    • 目标包。
    • 目标类。
    • 会员的名字。
    • 进一步插入自己的。
  • 在上述内容之后为Permission类建模,默认Policy配置可以表达,默认SecurityManager可以强制执行。

<强>非目标

  • 自动阻止特权升级,例如,特权代码反射性地调用某些方法,利用该方法执行的副作用。
  • 在单个隔离单元内强制执行此类约束。
  • 关心表现。

我已经玩了上面的一点,here's结果,这不是完美的,甚至没有错误,但希望足以证明这个想法。

简要概述(有关详细信息,请参阅Javadoc)

  • SelectiveReflectPermission是授予不受信任代码的权限。虽然无法容纳当前形式的进一步授权属性,但修改它以包含它们应该相当简单。
  • AllUnsafePermission表示一组不得授予不受信任的代码的权限(除非沙箱完整性无关紧要)。
  • DeniedPermissionDenyingPolicy属于同一类here,对前者进行少量修改,以便包含&#34; target&#34;具有无参数构造函数的权限。这些类只增加了表达&#34; deny-like&#34;策略配置中的规则,但没有必要。
  • 在检查ReflectionGatekeeper之后,
  • SelectiveReflectPermission委托了一些最常用的反射API部分。相同的逻辑可以嵌入到由核心反射调用的自定义SecurityManager相关方法(主要是#checkPermission(Permission))中,客户端代码非常欣赏,而不是必须使用不同的API。但是(那里总是#34;但是&#34;)我选择了这个选项,因为它需要脆弱的&#34;来电敏感&#34;要引入实现的代码,它必须通过调查调用堆栈来收集所需的授权属性,还要考虑递归反射调用,以及多个线程的调用......导致令人惊讶的接近于J2SE之前的东西1.2安全经理。此外,作为一种品味,我更喜欢管理者尽可能少地包含实际的授权逻辑 - 理想情况下它只能作为AccessController,最终成为Policy代表,没有捷径或漏洞

<强>用法

有某种应用程序启动器,或敏感组件的初始化逻辑本身建立安全性(除非成功,否则失败);这看起来如下:

package com.example.trusted.launcher;

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.Policy;
import java.util.Collections;

import com.example.trusted.security.DenyingPolicy;

public class Launcher {

    public static void main(String... args) throws Exception {

        /*
         * Get the actual main application class, which has hopefully been loaded by a different ClassLoader,
         * and resides in a distinct package and ProtectionDomain.
         */
        Class<?> realMain = getMainFromArgs(args);

        // install a Policy and SecurityManager

        /*
         * To avoid having to administer .policy configurations at the file system, you could bundle a
         * template with your app/lib, replace any CodeSource URLs you don't know until runtime, temp-save
         * to the file system (or use some transient URLStreamHandler) and "feed" the corresponding URL to
         * the Policy provider. Or better yet, if you can spare the time, implement your own Policy provider
         * as a mutable data structure.
         */
        String policyConfig = new String(
                Files.readAllBytes(Paths.get(Launcher.class.getResource("policy_template.txt").toURI())),
                StandardCharsets.UTF_8);

        // replace any CodeSource URL placeholders (e.g. with realMain's cs URL)
        policyConfig = adjustPolicyConfig(policyConfig);

        // temp-save it and hand it over to Policy
        Path tmpPolicyFile = Files.createTempFile("policy", ".tmp");
        Files.write(tmpPolicyFile, Collections.singletonList(policyConfig));

        // leading equals sign ensures only the indicated config gets loaded
        System.setProperty("java.security.policy", "=" + tmpPolicyFile.toUri());

        // unnecessary if you don't care about deny rules
        Policy.setPolicy(new DenyingPolicy());

        System.setSecurityManager(new SecurityManager());

        Files.delete(tmpPolicyFile);

        // filter args and call real main
        invokeMain(realMain, args);
    }

    // ...

}

...虽然潜在的政策配置模板可能是:

// note: curly braces are MessageFormat-escaped

// ---

// trusted code
grant codeBase "{0}" '{'

    permission java.security.AllPermission;

'}';

// sandboxed code
grant codeBase "{1}" '{'

    // all permissions...
    permission java.security.AllPermission;

    // ...save for unsafe ones
    permission com.example.trusted.security.DeniedPermission "com.example.trusted.security.AllUnsafePermission";

    // ...with global reflective access via ReflectionGatekeeper
    permission com.example.trusted.security.SelectiveReflectPermission;

    // ...with the exception of system code and our own com.example.trusted package
    permission com.example.trusted.security.DeniedPermission "com.example.trusted.security.SelectiveReflectPermission:*!sun.*!*!*:*";
    permission com.example.trusted.security.DeniedPermission "com.example.trusted.security.SelectiveReflectPermission:*!com.sun.*!*!*:*";
    permission com.example.trusted.security.DeniedPermission "com.example.trusted.security.SelectiveReflectPermission:*!com.oracle.*!*!*:*";
    permission com.example.trusted.security.DeniedPermission "com.example.trusted.security.SelectiveReflectPermission:*!net.java.*!*!*:*";
    permission com.example.trusted.security.DeniedPermission "com.example.trusted.security.SelectiveReflectPermission:*!java.*!*!*:*";
    permission com.example.trusted.security.DeniedPermission "com.example.trusted.security.SelectiveReflectPermission:*!javax.*!*!*:*";
    // currently it's not possible to express both a base package _and_ its sub-packages in a single permission
    permission com.example.trusted.security.DeniedPermission "com.example.trusted.security.SelectiveReflectPermission:*!com.example.trusted!*!*:*";
    permission com.example.trusted.security.DeniedPermission "com.example.trusted.security.SelectiveReflectPermission:*!com.example.trusted.*!*!*:*";

'}';

有了这两个部分,一些映射到域{1}的类将能够在其隔离单元外的Class上调用核心反射方法;但是 能够使用ReflectionGatekeeper作为等价物,在所有情况下除了明确拒绝的那些。