【问题标题】:Java Policy file - Deny permissions to a codebaseJava 策略文件 - 拒绝对代码库的权限
【发布时间】:2011-06-27 13:49:49
【问题描述】:

在 Java 策略文件中,grant codeBase 语法指定应授予哪个代码库哪些权限。例如,

grant codeBase "file:/C:/abc.jar" { 允许 java.security.AllPermission; };

AllPermission 授予 abc.jar 中的代码

以类似的方式,有没有办法deny 对特定语法的权限?像这样:

拒绝 codeBase "file:/C:/def.jar" { 允许 java.io.FilePermission; };

以便def.jar 中的代码获得除 FilePermission 之外的所有其他权限?

这可能吗?

我知道这可以使用SecurityManager 类轻松完成,但我只想知道仅使用策略文件是否可以做到这一点。

【问题讨论】:

    标签: java securitymanager policyfiles


    【解决方案1】:

    我意识到这已经晚了将近一年,但我想我正在尝试做类似的事情。

    有一种方法可以设置运行时权限,这样 Java 就不会授予全局权限。然后,您可以只指定您希望为您的应用授予的权限。关键是使用以下选项运行您的应用程序。

    java -Djava.security.manager -Djava.security.policy==policyFile.txt MyClass
    

    注意双精度等于-Djava.security.policy==policyFile.txt。双等号== 表示使用命名文件中的权限,而单等号-Djava.security.policy=policyFile.txt 表示除了继承的全局权限之外还使用这些权限。

    然后创建一个策略文件,排除您要拒绝的权限:

    // policyFile.txt
    grant codeBase "file:/C:/abc.jar" {
    
        // list of permissions minus the ones you want to deny
        // for example, the following would give the application
        // ONLY AudioPermission and AWTPermission.  Other
        // permissions such as java.io.FilePermission would be
        // denied.
    
        permission javax.sound.sampled.AudioPermission;
        permission java.awt.AWTPermission;
    
    }
    

    【讨论】:

    • 我意识到这是又过了 2 年......“拒绝”的意义在于,不可能知道未知的“允许”的完整列表,比如托管应用程序服务器,应用程序。例如,您想禁止写入“java.*”系统属性,但应用程序可以使用所有其他属性。正是出于这个原因,“拒绝代码库”会很好。这就是我打算做的......
    • 我试图通过在grant { 块内添加permission java.io.FilePermission "/home/temp/*", ""; 来拒绝用户读取除我的主目录之外的任何文件。我使用== 来指定策略文件,但它允许我从其他目录读取文件,甚至以当前用户身份执行chmod 之类的命令。我的理解正确吗?
    【解决方案2】:

    您可以使用Prograde 库,它实现了带有拒绝规则的策略文件。

    将以下 Maven 依赖项添加到您的应用中

    <dependency>
        <groupId>net.sourceforge.pro-grade</groupId>
        <artifactId>pro-grade</artifactId>
        <version>1.0</version>
    </dependency>
    

    然后使用标准系统属性为您的应用程序启用它:

    -Djava.security.manager=net.sourceforge.prograde.sm.ProgradeSecurityManager -Djava.security.policy==/path/to/your/application.policy
    

    或者您可以在代码中以编程方式替换 Policy 实现:

    System.setProperty("java.security.policy","/path/to/your/application.policy");
    Policy.setPolicy(new ProgradePolicyFile());
    

    策略文件的语法与标准实现类似,但您可以使用deny 代替grant,也可以使用关键字priority 更改优先级(默认值为"deny" - 保持向后兼容的)。

    例如,你可以做某事。喜欢:

    grant {
        permission java.lang.RuntimePermission "*";
    };
    
    deny {
        permission java.lang.RuntimePermission "exitVM.*";
    };
    

    其他例子是here

    【讨论】:

    • 即使直接调用System.exit(0); 失败,我敢打赌你可以使用另一个RuntimePermissions 来获得exitVM.* 的等价物。
    【解决方案3】:

    没有。策略文件没有这样的实现。如果你真的很绝望,你可以编写自己的系统。

    【讨论】:

    【解决方案4】:

    获得拒绝规则支持的最少参与方法之一是:

    • 定义一个“否定的”Permission 子类,它包含一个常规的肯定权限并否定它;和
    • 包装默认的Policy,使其(其包装器)能够理解此类权限。

    DeniedPermission

    package com.example.q5003565;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Modifier;
    import java.security.BasicPermission;
    import java.security.Permission;
    import java.security.UnresolvedPermission;
    import java.text.MessageFormat;
    
    /**
     * A representation of a "negative" privilege.
     * <p>
     * A <code>DeniedPermission</code>, when "granted" to some <code>ProtectionDomain</code>, represents
     * a privilege which <em>cannot</em> be exercised, regardless of any positive permissions
     * (<code>AllPermission</code> included) possessed. In other words, if a set of granted permissions,
     * <em>P</em>, contains a permission of this class, <em>D</em>, then the set of effectively granted
     * permissions is<br/>
     * <br/>
     * &nbsp;&nbsp;&nbsp;&nbsp;<em>{ P<sub>implied</sub> - D<sub>implied</sub> }</em>.
     * </p>
     * <p>
     * Each instance of this class encapsulates a <em>target permission</em>, representing the
     * "positive" permission being negated.
     * </p>
     * Denied permissions employ the following naming scheme:<br/>
     * <br/>
     * &nbsp;&nbsp;&nbsp;&nbsp;<em>&lt;target_class_name&gt;:&lt;target_name&gt;(:&lt;target_actions&gt;)</em><br/>
     * <br/>
     * where:
     * <ul>
     * <li><em>&lt;target_class_name&gt;</em> is the fully qualified name of the target permission's
     * class,</li>
     * <li><em>&lt;target_name&gt;</em> is the {@linkplain #getName() name} of the target
     * permission,</li>
     * <li><em>(&lt;target_actions&gt;)</em> is, optionally, the {@linkplain #getActions() actions
     * string} of the target permission, and</li>
     * <li>the <em>':'</em> character stands for itself.</li>
     * </ul>
     * A denied permission, having a target permission <em>t</em>, is said to
     * {@linkplain #implies(Permission) <em>imply</em>} another permission <em>p</em>, iff:
     * <ul>
     * <li>p <em>is not</em> itself a denied permission, and <code>(t.implies(p) == true)</code>,
     * or</li>
     * <li>p <em>is</em> a denied permission, with a target <em>t1</em>, and
     * <code>(t.implies(t1) == true)</code>.</li>
     * </ul>
     * <p>
     * It is the responsibility of the policy decision point (e.g., the <code>Policy</code> provider) to
     * take denied permission semantics into account when issuing authorization statements.
     * </p>
     */
    public final class DeniedPermission extends BasicPermission {
    
        private static final String NULL_STR_ARG = "<null>", EMPTY_STR_ARG = "<empty> ";
        private static final long serialVersionUID = 2102974454790623344L;
    
        private final Permission target;
    
        /**
         * Instantiates a <code>DeniedPermission</code> that encapsulates a target permission of the
         * indicated class, specified name and, optionally, actions.
         * 
         * @throws IllegalArgumentException
         *             if:
         *             <ul>
         *             <li><code>targetClassName</code> is <code>null</code>, the empty string, does not
         *             refer to a concrete <code>Permission</code> descendant, or refers to
         *             <code>DeniedPermission.class</code> or <code>UnresolvedPermission.class</code>.</li>
         *             <li><code>targetName</code> is <code>null</code>.</li>
         *             <li><code>targetClassName</code> cannot be instantiated, and it's the caller's fault;
         *             e.g., because <code>targetName</code> and/or <code>targetActions</code> do not adhere
         *             to the naming constraints of the target class; or due to the target class not
         *             exposing a <code>(String name)</code>, or <code>(String name, String actions)</code>
         *             constructor, depending on whether <code>targetActions</code> is <code>null</code> or
         *             not.</li>
         *             </ul>
         * @throws SecurityException
         *             if a <code>SecurityManager</code>, <code>sm</code>, is installed, and the invocation
         *             <code>sm.checkPackageAccess(targetClassPackageName)</code> (where
         *             <code>targetClassPackageName</code> is the package of the class referred to
         *             by <code>targetClassName</code>) denies access.
         */
        public static DeniedPermission newDeniedPermission(String targetClassName, String targetName,
                String targetActions) {
            if (targetClassName == null || targetClassName.trim().isEmpty() || targetName == null) {
                throw new IllegalArgumentException("[targetClassName] and [targetName] must not be null or empty.");
            }
            StringBuilder sb = new StringBuilder(targetClassName).append(":").append(targetName);
            if (targetName != null) {
                sb.append(":").append(targetName);
            }
            return new DeniedPermission(sb.toString());
        }
    
        /**
         * Instantiates a <code>DeniedPermission</code> that encapsulates the given target permission.
         * 
         * @throws IllegalArgumentException
         *             if <code>target</code> is <code>null</code>, a <code>DeniedPermission</code>, or an
         *             <code>UnresolvedPermission</code>.
         */
        public static DeniedPermission newDeniedPermission(Permission target) {
            if (target == null) {
                throw new IllegalArgumentException("[target] must not be null.");
            }
            if (target instanceof DeniedPermission || target instanceof UnresolvedPermission) {
                throw new IllegalArgumentException("[target] must not be a DeniedPermission or an UnresolvedPermission.");
            }
            StringBuilder sb = new StringBuilder(target.getClass().getName()).append(":").append(target.getName());
            String targetActions = target.getActions();
            if (targetActions != null) {
                sb.append(":").append(targetActions);
            }
            return new DeniedPermission(sb.toString(), target);
        }
    
        private static Permission constructTargetPermission(String targetClassName, String targetName,
                String targetActions) {
            Class<?> targetClass;
            try {
                targetClass = Class.forName(targetClassName);
            }
            catch (ClassNotFoundException cnfe) {
                if (targetClassName.trim().isEmpty()) {
                    targetClassName = EMPTY_STR_ARG;
                }
                throw new IllegalArgumentException(
                        MessageFormat.format("Target Permission class [{0}] not found.", targetClassName));
            }
            if (!Permission.class.isAssignableFrom(targetClass) || Modifier.isAbstract(targetClass.getModifiers())) {
                throw new IllegalArgumentException(MessageFormat
                        .format("Target Permission class [{0}] is not a (concrete) Permission.", targetClassName));
            }
            if (targetClass == DeniedPermission.class || targetClass == UnresolvedPermission.class) {
                throw new IllegalArgumentException(
                        "Target Permission class must not be a DeniedPermission itself, nor an UnresolvedPermission.");
            }
            Constructor<?> targetCtor;
            try {
                if (targetActions == null) {
                    targetCtor = targetClass.getConstructor(String.class);
                }
                else {
                    targetCtor = targetClass.getConstructor(String.class, String.class);
                }
            }
            catch (NoSuchMethodException nsme) {
                throw new IllegalArgumentException(MessageFormat.format(
                        "Target Permission class [{0}]  (String name) or (String name, String actions) constructor.",
                        targetClassName));
            }
            try {
                return (Permission) targetCtor
                        .newInstance(((targetCtor.getParameterCount() == 1) ? new Object[] { targetName }
                                : new Object[] { targetName, targetActions }));
            }
            catch (ReflectiveOperationException roe) {
                if (roe instanceof InvocationTargetException) {
                    if (targetName == null) {
                        targetName = NULL_STR_ARG;
                    }
                    else if (targetName.trim().isEmpty()) {
                        targetName = EMPTY_STR_ARG;
                    }
                    if (targetActions == null) {
                        targetActions = NULL_STR_ARG;
                    }
                    else if (targetActions.trim().isEmpty()) {
                        targetActions = EMPTY_STR_ARG;
                    }
                    throw new IllegalArgumentException(MessageFormat.format(
                            "Could not instantiate target Permission class [{0}]; provided target name [{1}] and/or target [{2}] actions potentially erroneous.",
                            targetClassName, targetName, targetActions), roe);
                }
                throw new RuntimeException(MessageFormat.format(
                        "Could not instantiate target Permission class [{0}] - an unforeseen error occurred, see attached cause for details.",
                        targetClassName), roe);
            }
        }
    
        /**
         * Instantiates a <code>DeniedPermission</code> that encapsulates a target permission of the class,
         * name and, optionally, actions, collectively provided as the <code>name</code> argument.
         * 
         * @throws IllegalArgumentException
         *             if:
         *             <ul>
         *             <li><code>name</code>'s target permission class name component is empty, does not
         *             refer to a concrete <code>Permission</code> descendant, or refers to
         *             <code>DeniedPermission.class</code> or <code>UnresolvedPermission.class</code>.</li>
         *             <li><code>name</code>'s target name component is <code>empty</code></li>
         *             <li>the target permission class cannot be instantiated, and it's the caller's fault;
         *             e.g., because <code>name</code>'s target name and/or target actions component(s) do
         *             not adhere to the naming constraints of the target class; or due to the target class
         *             not exposing a <code>(String name)</code>, or
         *             <code>(String name, String actions)</code> constructor, depending on whether the
         *             target actions component is empty or not.</li>
         *             </ul>
         * @throws SecurityException
         *             if a <code>SecurityManager</code>, <code>sm</code>, is installed, and the invocation
         *             <code>sm.checkPackageAccess(targetClassPackageName)</code>
         *             (where <code>targetClassPackageName</code> is the package of the class referred to
         *             by <code>name</code>'s target name component) denies access.
         */
        public DeniedPermission(String name) {
            super(name);
            String[] comps = name.split(":");
            if (comps.length < 2) {
                throw new IllegalArgumentException(MessageFormat.format("Malformed [name] argument: {0}", name));
            }
            this.target = constructTargetPermission(comps[0], comps[1], ((comps.length < 3) ? null : comps[2]));
        }
    
        private DeniedPermission(String name, Permission target) {
            super(name);
            this.target = target;
        }
    
        /**
         * Checks whether the given permission is implied by this one, as per the
         * {@linkplain DeniedPermission overview}.
         */
        @Override
        public boolean implies(Permission p) {
            if (p instanceof DeniedPermission) {
                return target.implies(((DeniedPermission) p).target);
            }
            return target.implies(p);
        }
    
        /**
         * Returns this denied permission's target permission.
         */
        public Permission getTargetPermission() {
            return target;
        }
    
    }
    

    DenyingPolicy

    package com.example.q5003565;
    
    import java.security.AccessController;
    import java.security.CodeSource;
    import java.security.NoSuchAlgorithmException;
    import java.security.NoSuchProviderException;
    import java.security.Permission;
    import java.security.PermissionCollection;
    import java.security.Policy;
    import java.security.PrivilegedAction;
    import java.security.ProtectionDomain;
    import java.security.UnresolvedPermission;
    import java.util.Enumeration;
    
    /**
     * Wrapper that adds rudimentary {@link DeniedPermission} processing capabilities to the standard
     * file-backed <code>Policy</code>.
     */
    public final class DenyingPolicy extends Policy {
    
        /*
         * doPrivileged needed just in case there's already a SecurityManager installed at class loading
         * time.
         */
        private static final ProtectionDomain OWN_PD = AccessController
                .doPrivileged((PrivilegedAction<ProtectionDomain>) DenyingPolicy.class::getProtectionDomain);
    
        private final Policy defaultPolicy;
    
        {
            try {
                // will fail unless the calling acc has SecurityPermission "createPolicy.javaPolicy"
                defaultPolicy = Policy.getInstance("javaPolicy", null, "SUN");
            }
            catch (NoSuchProviderException | NoSuchAlgorithmException e) {
                throw new RuntimeException("Could not acquire default Policy.", e);
            }
        }
    
        @Override
        public PermissionCollection getPermissions(CodeSource codesource) {
            return defaultPolicy.getPermissions(codesource);
        }
    
        @Override
        public PermissionCollection getPermissions(ProtectionDomain domain) {
            return defaultPolicy.getPermissions(domain);
        }
    
        /**
         * @return <code>true</code> iff:
         *         <ul>
         *         <li><code>permission</code> <em>is not</em> an instance of
         *         <code>DeniedPermission</code>,</li>
         *         <li>an <code>implies(domain, permission)</code> invocation on the system-default
         *         <code>Policy</code> yields <code>true</code>, and</li>
         *         <li><code>permission</code> <em>is not</em> implied by any <code>DeniedPermission</code>s
         *         having potentially been assigned to <code>domain</code>.</li>
         *         </ul>
         */
        @Override
        public boolean implies(ProtectionDomain domain, Permission permission) {
            if (OWN_PD.equals(domain)) {
                /*
                 * Recursive invocation due to a privilege-requiring method we invoked. If you're uncomfortable with
                 * this, get rid of it and grant (via .policy) a RuntimePermission "accessClassInPackage.*" to
                 * OWN_PD.
                 */
                return true;
            }
            if (permission instanceof DeniedPermission) {
                /*
                 * At the policy decision level, DeniedPermissions can only themselves imply, not be implied (as
                 * they take away, rather than grant, privileges). Returning true for a deny rule would be
                 * more confusing than convenient.
                 */
                return false;
            }
    
            if (!defaultPolicy.implies(domain, permission)) {
                // permission not granted--no need to check whether denied
                return false;
            }
    
            /*
             * Permission granted--now check whether there's an overriding DeniedPermission. The following
             * assumes that defaultPolicy (its wrapped PolicySpi) is a sun.security.provider.PolicySpiFile
             * (other implementations might not support #getPermissions(ProtectionDomain)
             * and/or handle resolution of UnresolvedPermissions differently).
             */
    
            Enumeration<Permission> perms = defaultPolicy.getPermissions(domain).elements();
            while (perms.hasMoreElements()) {
                Permission p = perms.nextElement();
                /*
                 * DeniedPermissions will generally remain unresolved, as no code is expected to check whether other
                 * code has been "granted" such a permission.
                 */
                if (p instanceof UnresolvedPermission) {
                    UnresolvedPermission up = (UnresolvedPermission) p;
                    if (up.getUnresolvedType().equals(DeniedPermission.class.getName())) {
                        // force resolution
                        defaultPolicy.implies(domain, up);
                        // evaluate right away, to avoid reiterating over the collection
                        p = AccessController.doPrivileged(
                                (PrivilegedAction<Permission>) () -> new DeniedPermission(up.getUnresolvedName()));
                    }
                }
                if (p instanceof DeniedPermission && p.implies(permission)) {
                    // permission denied
                    return false;
                }
            }
            // permission granted
            return true;
        }
    
        @Override
        public void refresh() {
            defaultPolicy.refresh();
        }
    
    }
    

    用法

    只需将 DeniedPermissions 嵌入到普通的旧 grant 规则中;例如,以下规则将授予一切读取名称以“user.”开头的系统属性的能力,到 some.jar 的类。

    grant codeBase "file:/home/your_user/classpath/some.jar" {
        permission java.security.AllPermission;
        permission com.example.q5003565.DeniedPermission "java.util.PropertyPermission:user.*:read";
    };
    

    然后通过Policy.setPolicy(new DenyingPolicy()); 安装DenyingPolicy

    警告:虽然在语义上是正确的,正如之前的回答的评论中提到的那样,上面的例子是无效的,因为它仍然授予危险的权限,例如SecurityPermission "setPolicy" ,它隐含地允许沙盒代码做任何它喜欢的事情,包括DeniedPermission 禁止的操作。为了防止这种情况发生,而不是从AllPermission 中减去权限,而是考虑从AllSafePermission 中减去,其中AllSafePermission 被定义为implies 所有除了 已知的沙盒击败权限1.

    备注

    • 任何权限都可以被拒绝权限包装,只要它遵循标准目标名称-操作约定,公开(String) 和/或(String, String) 构造函数,并适当地覆盖implies(Permission)
    • 一次拒绝多个权限:
      • 创建一个普通的权限子类implies要拒绝的权限。
      • 从策略配置“授予”被拒绝的权限,进而引用您的实施实例。
    • DenyingPolicy阻止授予分配给保护域的权限statically(例如默认授予源自类路径的代码的RuntimePermission "exitVM.*"),因为通常,此类权限的评估发生在策略维护的权限评估之前。为了也拒绝任何这些权限,您必须将ClassLoader 替换为:
      • 要么一开始就不授予权限,要么
      • maps 类加载到 ProtectionDomain 子类的实例,覆盖 implies(Permission) 使得:
        • 它总是委托给策略,或者
        • 以类似于DenyingPolicy的方式处理DeniedPermissions。

    1:有关此类权限的列表,请参见例如Maass, M. (2016). A Theory and Tools for Applying Sandboxes Effectively.,表 3.1(第 47 页)。

    【讨论】:

      猜你喜欢
      • 2011-01-02
      • 2019-11-09
      • 2017-12-04
      • 2020-02-23
      • 2022-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-09
      相关资源
      最近更新 更多