Java策略文件 – 拒绝对代码库的权限

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

grant codeBase“file:/ C:/abc.jar”{permission java.security.AllPermission; };

AllPermission授予abc.jar中的代码

以类似的方式,有没有办法deny特定语法的权限? 喜欢这个:

deny codeBase“file:/ C:/def.jar”{permission java.io.FilePermission; };

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

这有可能吗?

我知道这可以使用SecurityManager类轻松完成,但我只想知道是否可以通过仅使用策略文件来实现。

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

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

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

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

请注意,double等于-Djava.security.policy==policyFile.txt 。 double equals ==表示使用命名文件中的权限而不是单个等号-Djava.security.policy=policyFile.txt ,这意味着除了inheritance的全局权限之外还使用这些权限。

然后创建一个策略文件,不包括您要拒绝的权限:

 // 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; } 

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

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

  net.sourceforge.pro-grade pro-grade 1.0  

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

 -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更改优先priority (默认值为"deny" – 保持向后兼容)。

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

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

其他例子在这里 。

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

  • 定义一个“负”的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. * 

* A DeniedPermission, when "granted" to some ProtectionDomain, represents * a privilege which cannot be exercised, regardless of any positive permissions * (AllPermission included) possessed. In other words, if a set of granted permissions, * P, contains a permission of this class, D, then the set of effectively granted * permissions is
*
*     { Pimplied - Dimplied }. *

*

* Each instance of this class encapsulates a target permission, representing the * "positive" permission being negated. *

* Denied permissions employ the following naming scheme:
*
*     <target_class_name>:<target_name>(:<target_actions>)
*
* where: *
    *
  • <target_class_name> is the fully qualified name of the target permission's * class,
  • *
  • <target_name> is the {@linkplain #getName() name} of the target * permission,
  • *
  • (<target_actions>) is, optionally, the {@linkplain #getActions() actions * string} of the target permission, and
  • *
  • the ':' character stands for itself.
  • *
* A denied permission, having a target permission t, is said to * {@linkplain #implies(Permission) imply} another permission p, iff: *
    *
  • p is not itself a denied permission, and (t.implies(p) == true), * or
  • *
  • p is a denied permission, with a target t1, and * (t.implies(t1) == true).
  • *
*

* It is the responsibility of the policy decision point (eg, the Policy provider) to * take denied permission semantics into account when issuing authorization statements. *

*/ public final class DeniedPermission extends BasicPermission { private static final String NULL_STR_ARG = "", EMPTY_STR_ARG = " "; private static final long serialVersionUID = 2102974454790623344L; private final Permission target; /** * Instantiates a DeniedPermission that encapsulates a target permission of the * indicated class, specified name and, optionally, actions. * * @throws IllegalArgumentException * if: *
    *
  • targetClassName is null, the empty string, does not * refer to a concrete Permission descendant, or refers to * DeniedPermission.class or UnresolvedPermission.class.
  • *
  • targetName is null.
  • *
  • targetClassName cannot be instantiated, and it's the caller's fault; * eg, because targetName and/or targetActions do not adhere * to the naming constraints of the target class; or due to the target class not * exposing a (String name), or (String name, String actions) * constructor, depending on whether targetActions is null or * not.
  • *
* @throws SecurityException * if a SecurityManager, sm, is installed, and the invocation * sm.checkPackageAccess(targetClassPackageName) (where * targetClassPackageName is the package of the class referred to * by targetClassName) 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 DeniedPermission that encapsulates the given target permission. * * @throws IllegalArgumentException * if target is null, a DeniedPermission, or an * UnresolvedPermission. */ 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 DeniedPermission that encapsulates a target permission of the class, * name and, optionally, actions, collectively provided as the name argument. * * @throws IllegalArgumentException * if: *
    *
  • name's target permission class name component is empty, does not * refer to a concrete Permission descendant, or refers to * DeniedPermission.class or UnresolvedPermission.class.
  • *
  • name's target name component is empty
  • *
  • the target permission class cannot be instantiated, and it's the caller's fault; * eg, because name'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 (String name), or * (String name, String actions) constructor, depending on whether the * target actions component is empty or not.
  • *
* @throws SecurityException * if a SecurityManager, sm, is installed, and the invocation * sm.checkPackageAccess(targetClassPackageName) * (where targetClassPackageName is the package of the class referred to * by name'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 Policy. */ 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) 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 true iff: * 
    *
  • permission is not an instance of * DeniedPermission,
  • *
  • an implies(domain, permission) invocation on the system-default * Policy yields true, and
  • *
  • permission is not implied by any DeniedPermissions * having potentially been assigned to domain.
  • *
*/ @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 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) () -> 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(); } }

用法

只是将DeniedPermission嵌入普通的旧授权规则中; 例如,以下规则将授予除some.jar的类以及以“user。”开头的名称读取系统属性的能力。

 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());安装Policy.setPolicy(new DenyingPolicy());

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

笔记

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

1:有关此类权限的列表,请参阅例如Maass,M。(2016)。 有效应用沙箱的理论与工具。 ,表3.1(第47页)。