Java Language
Gerente de seguridad
Buscar..
Habilitando el SecurityManager
Las máquinas virtuales Java (JVM) se pueden ejecutar con un SecurityManager instalado. El SecurityManager gobierna lo que se permite que haga el código que se ejecuta en la JVM, en función de factores tales como desde dónde se cargó el código y qué certificados se usaron para firmar el código.
El SecurityManager se puede instalar configurando la propiedad del sistema java.security.manager en la línea de comandos al iniciar la JVM:
java -Djava.security.manager <main class name>
o programáticamente desde el código de Java:
System.setSecurityManager(new SecurityManager())
El Java SecurityManager estándar otorga permisos sobre la base de una Política, que se define en un archivo de políticas. Si no se especifica ningún archivo de políticas, se utilizará el archivo de políticas predeterminado en $JAVA_HOME/lib/security/java.policy
.
Clases de sandboxing cargadas por un ClassLoader
El ClassLoader debe proporcionar un dominio de ProtectionDomain
identifique la fuente del código:
public class PluginClassLoader extends ClassLoader {
private final ClassProvider provider;
private final ProtectionDomain pd;
public PluginClassLoader(ClassProvider provider) {
this.provider = provider;
Permissions permissions = new Permissions();
this.pd = new ProtectionDomain(provider.getCodeSource(), permissions, this, null);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classDef = provider.getClass(name);
Class<?> clazz = defineClass(name, classDef, 0, classDef.length, pd);
return clazz;
}
}
Al reemplazar findClass
lugar de loadClass
el modelo delegacional se conserva, y el PluginClassLoader primero consultará el sistema y el cargador de clases principal para las definiciones de clase.
Crear una política:
public class PluginSecurityPolicy extends Policy {
private final Permissions appPermissions = new Permissions();
private final Permissions pluginPermissions = new Permissions();
public PluginSecurityPolicy() {
// amend this as appropriate
appPermissions.add(new AllPermission());
// add any permissions plugins should have to pluginPermissions
}
@Override
public Provider getProvider() {
return super.getProvider();
}
@Override
public String getType() {
return super.getType();
}
@Override
public Parameters getParameters() {
return super.getParameters();
}
@Override
public PermissionCollection getPermissions(CodeSource codesource) {
return new Permissions();
}
@Override
public PermissionCollection getPermissions(ProtectionDomain domain) {
return isPlugin(domain)?pluginPermissions:appPermissions;
}
private boolean isPlugin(ProtectionDomain pd){
return pd.getClassLoader() instanceof PluginClassLoader;
}
}
Finalmente, configure la política y un SecurityManager (la implementación predeterminada está bien):
Policy.setPolicy(new PluginSecurityPolicy());
System.setSecurityManager(new SecurityManager());
Implementando reglas de negación de políticas.
Ocasionalmente es conveniente negar un cierto Permission
a un dominio de ProtectionDomain
, independientemente de cualquier otro permiso que acumule el dominio. Este ejemplo muestra solo uno de todos los enfoques posibles para satisfacer este tipo de requisitos. Introduce una clase de permiso "negativa", junto con un envoltorio que permite que la Policy
predeterminada se reutilice como un repositorio de dichos permisos.
Notas:
- La sintaxis y el mecanismo de archivo de política estándar para la asignación de permisos en general no se ven afectados. Esto significa que las reglas de denegación dentro de los archivos de políticas aún se expresan como concesiones .
- El contenedor de políticas está destinado a encapsular específicamente la
Policy
respaldada por archivos predeterminada (se supone que escom.sun.security.provider.PolicyFile
). - Los permisos denegados solo se procesan como tales en el nivel de la política. Si se asignan estáticamente a un dominio, de forma predeterminada serán tratados por ese dominio como permisos "positivos" ordinarios.
La clase de DeniedPermission
package com.example;
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> and/or
* <code>Principal</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/>
* <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 denied.
* </p>
* Denied permissions employ the following naming scheme:<br/>
* <br/>
* <em><target_class_name>:<target_name>(:<target_actions>)</em><br/>
* <br/>
* where:
* <ul>
* <li><em>target_class_name</em> is the name of the target permission's class,</li>
* <li><em>target_name</em> is the name of the target permission, and</li>
* <li><em>target_actions</em> is, optionally, the actions string of the target permission.</li>
* </ul>
* A denied permission, having a target permission <em>t</em>, is said to <em>imply</em> another
* permission <em>p</em>, if:
* <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>.
* </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 final Permission target;
private static final long serialVersionUID = 473625163869800679L;
/**
* 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>
*/
public static DeniedPermission newDeniedPermission(String targetClassName, String targetName,
String targetActions) {
if (targetClassName == null || targetClassName.trim().isEmpty() || targetName == null) {
throw new IllegalArgumentException(
"Null or empty [targetClassName], or null [targetName] argument was supplied.");
}
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 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>
*/
public DeniedPermission(String name) {
super(name);
String[] comps = name.split(":");
if (comps.length < 2) {
throw new IllegalArgumentException(MessageFormat.format("Malformed name [{0}] argument.", name));
}
this.target = initTarget(comps[0], comps[1], ((comps.length < 3) ? null : comps[2]));
}
/**
* 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("Null [target] argument.");
}
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 DeniedPermission(String name, Permission target) {
super(name);
this.target = target;
}
private Permission initTarget(String targetClassName, String targetName, String targetActions) {
Class<?> targetClass;
try {
targetClass = Class.forName(targetClassName);
}
catch (ClassNotFoundException cnfe) {
if (targetClassName.trim().isEmpty()) {
targetClassName = "<empty>";
}
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 cannot be a DeniedPermission itself.");
}
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}] does not provide or expose a (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>";
}
else if (targetName.trim().isEmpty()) {
targetName = "<empty>";
}
if (targetActions == null) {
targetActions = "<null>";
}
else if (targetActions.trim().isEmpty()) {
targetActions = "<empty>";
}
throw new IllegalArgumentException(MessageFormat.format(
"Could not instantiate target Permission class [{0}]; provided target name [{1}] and/or target actions [{2}] potentially erroneous.",
targetClassName, targetName, targetActions), roe);
}
throw new RuntimeException(
"Could not instantiate target Permission class [{0}]; an unforeseen error occurred - see attached cause for details",
roe);
}
}
/**
* Checks whether the given permission is implied by this one, as per the {@link 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 (the actual positive permission which is not
* to be granted).
*/
public Permission getTargetPermission() {
return target;
}
}
La clase DenyingPolicy
package com.example;
import java.security.CodeSource;
import java.security.NoSuchAlgorithmException;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Policy;
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 {
{
try {
defaultPolicy = Policy.getInstance("javaPolicy", null);
}
catch (NoSuchAlgorithmException nsae) {
throw new RuntimeException("Could not acquire default Policy.", nsae);
}
}
private final Policy defaultPolicy;
@Override
public PermissionCollection getPermissions(CodeSource codesource) {
return defaultPolicy.getPermissions(codesource);
}
@Override
public PermissionCollection getPermissions(ProtectionDomain domain) {
return defaultPolicy.getPermissions(domain);
}
/**
* @return
* <ul>
* <li><code>true</code> if:</li>
* <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>
* <li><code>false</code>, otherwise.
* </ul>
*/
@Override
public boolean implies(ProtectionDomain domain, Permission permission) {
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). Furthermore, clients aren't supposed to use this
* method for checking whether some domain _does not_ have a permission (which is what
* DeniedPermissions express after all).
*/
return false;
}
if (!defaultPolicy.implies(domain, permission)) {
// permission not granted, so no need to check whether denied
return false;
}
/*
* Permission granted--now check whether there's an overriding DeniedPermission. The following
* assumes that previousPolicy is a sun.security.provider.PolicyFile (different implementations
* might not support #getPermissions(ProtectionDomain) and/or handle 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 = 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();
}
}
Manifestación
package com.example;
import java.security.Policy;
public class Main {
public static void main(String... args) {
Policy.setPolicy(new DenyingPolicy());
System.setSecurityManager(new SecurityManager());
// should fail
System.getProperty("foo.bar");
}
}
Asignar algunos permisos:
grant codeBase "file:///path/to/classes/bin/-"
permission java.util.PropertyPermission "*", "read,write";
permission com.example.DeniedPermission "java.util.PropertyPermission:foo.bar:read";
};
Por último, ejecute el Main
y observe cómo falla, debido a la regla de "denegar" ( DeniedPermission
) que DeniedPermission
la grant
(su PropertyPermission
). Tenga en cuenta que una setProperty("foo.baz", "xyz")
lugar habría tenido éxito, ya que el permiso denegado solo cubre la acción "leer", y solo para la propiedad "foo.bar".