Escribir un Controlador de Seguridad

Un controlador de seguridad es un objeto de la Máquina Virtual JavaTM (JVM) que implementa un policía de seguridad. Por defecto, la plataforma Java 2® proporciona un controlador de seguridad que desactiva todos los accesos a los recursos del sistema local menos los accesos de lectura al directorio y sus subdirectorios dónde es invocado el programa.

Podemos extender el controlador de seguridad por defecto para implementar verificaciones y aprovaciones personalizadas para applets y aplicaciones, pero la implementación debe incluir código de verificación de accesos apropiado para cada método checkXXX que sobreescribamos. Si no incluimos este código, no sucederá ningun chequeo de verificación, y nuestro programa escindirá el fichero de policía del sistema.

Esta sección usa una aplicación de ejemplo para explicar cómo escribir un controlador de seguridad personalizado antes de leer y escribir los ficheros especificados. La implementación incluye código de verificación de accesos por eso una vez que el usuario pasa el chequeo de password, todavía necesita que el fichero tenga permisos de lectura y escritua en su fichero de policía.

El ejemplo consiste en la aplicación FileIO, y el programa PasswordSecurityManager que proporciona la implementación del controlador de seguridad personalizado.


El programa FileIO

El programa FileIO muestra un sencillo interface de usuario que pide al usuario que introduzca algún texto. Cuando el usario pulsa el botón Click Me, el texto se graba en un fichero en el directorio home del usuario, y se abre y se lee un segundo fichero. El texto leído del segundo fichero se muestra al usuario.

Antes de Pulsar el botón

Después de Pulsar el botón

El controlador de seguridad personalizado para este programa le pude al usuario final que introduzca una password antes de permitir que FileIO escriba o lea texto desde un fichero. El método main de FileIO crea un controlador de seguridad personalizado llamando PasswordSecurityManager.

public static void main(String[] args){
  BufferedReader buffy = new BufferedReader(
      new InputStreamReader(System.in));
  try {
    System.setSecurityManager(
      new PasswordSecurityManager("pwd", buffy));
  } catch (SecurityException se) {
    System.err.println("SecurityManager already set!");
  }

La Clases PasswordSecurityManager

La clase PasswordSecurityManager declara dos variables de ejemplar privadas, que son inicializadas por el constructor cuando se instala el controlador de seguridad personalziado. La variable de ejemplar password contiene el password real, y la variable de ejemplar buffy es un buffer de entrada que almacena la password de entrada del usuario final.
public class PasswordSecurityManager 
               extends SecurityManager{

 private String password;
 private BufferedReader buffy;

 public PasswordSecurityManager(String p, 
          BufferedReader b){
   super();
   this.password = p;
   this.buffy = b;
 }
El método accessOK le pide una password al usuario final, verifica la password, y devuelve true si el password es correcto y false si no lo es.
private boolean accessOK() {
  int c;
  String response;

  System.out.println("Password, please:");
  try {
    response = buffy.readLine();
    if (response.equals(password))
      return true;
    else
      return false;
  } catch (IOException e) {
    return false;
  }
}

Verificar Accesos

La clase padre SecurityManager proporciona métodos para verificar accesos de lectura y escritura a ficheros del sistema. Los método checkRead y checkWrite tienen una versión que acepta un String y otra versión que acepta un descriptor de ficero.

Este ejemplo sólo sobreescrie las versiones String para mantener el ejemplo sencillo, y como el programa FileIO usa accesos a directorios y ficheros como Strings.

public void checkRead(String filename) {
  if((filename.equals(File.separatorChar + "home" + 
	File.separatorChar + "monicap" + 
	File.separatorChar + "text2.txt"))){
  if(!accessOK()){
    super.checkRead(filename);
    throw new SecurityException("No Way!");
  } else {
    FilePermission perm = new FilePermission(
      File.separatorChar + "home" +
      File.separatorChar + "monicap" + 
      File.separatorChar + "text2.txt", "read");
      checkPermission(perm);
      }
   }
}

public void checkWrite(String filename) {
  if((filename.equals(File.separatorChar + "home" + 
		 File.separatorChar + "monicap" + 
		 File.separatorChar + "text.txt"))){
    if(!accessOK()){
      super.checkWrite(filename);
      throw new SecurityException("No Way!");
    } else {
      FilePermission perm = new FilePermission(
		File.separatorChar + "home" + 
		File.separatorChar + "monicap" + 
		File.separatorChar + "text.txt" , 
		"write");
            checkPermission(perm);
      }
    }
  }
}
El mértodo checkWrite es llamado antes de escribir la entrada del usuario en el fichero de salida. Esto es porque la clase FileOutputStream llama primero a SecurityManager.checkWrite.

La implementación personalizadapara SecurityManager.checkWrite chequea el pathname /home/monicap/text.txt, si es true le pide al usuario una password. Si la password es correcta, el método checkWriterealiza el chequeo del acceso creando un ejemplar del permiso requerido y pasandolo al método SecurityManager.checkPermission. Este chequeo sucederá si el controlador de seguirdad encuentra un fichero de seguridad de sistemam de usuario o de programa con el permiso especificado.

Una vez completada la operación de escritura, al usuario final se le pide la password dos veces más. La primera vez para leer el directorio /home/monicap, y la segunda vez para leer el fichero text2.txt. Se realiza un chequeo de acceso antes de que tenga lugar la operación de lectura.

Fichero de Policía

Aquñi estña el fichero de policía que necesita el programa FileIO para las operaciones de lectura y escritura. También conceder permiso al controlador de seguridad personalizado para acceder a la cola de eventos en representación de la aplicación y mostrar la ventana de la aplicación si ningún aviso.
grant {
  permission java.io.FilePermission 
        "${user.home}/text.txt", "write";
  permission java.util.PropertyPermission 
	"user.home", "read";
  permission java.io.FilePermission 
	"${user.home}/text2.txt", "read";
  permission java.awt.AWTPermission 
	"accessEventQueue";
  permission java.awt.AWTPermission 
	"showWindowWithoutWarningBanner";
};

Ejecutar el programa FileIO

Aquí está cómo ejecutar el programa FileIO con el fichero de policía:
 java -Djava.security.policy=polfile FileIO

Información de Referencia

El Apéndice A: Seguridad y Permisos describe los permisos disponibles y explica las consecuencias de conceder permisos. Una forma de usar esta es información es para ayudarnos a limitar los permisos concedidos a un applet o aplciación podrían necesitar ser ejecutados satisfactoriamente. Otra forma de usar esta información es educarnos en la forma en un permiso particular puede ser explotado por código mailicioso.

El Apéndice B: Clases, Métodos y Permisos proporciona lista de métodos de la plataforma Java 2 que están implementados para chequeos de seguridad, los permisos que cada uno requiere, y el método java.security.SecurityManager llamado para realizar el chequeo de accesos.

Podemos usar esta referencia para escribir implementaciones de nuestro propio controlador de seguridad o cundo implementamos métodos abstactos que realizan tareas relacionadas con la seguridad.

El Apéndide C: Métodos del SecurityManager lista los chequeos de permisos para los método de SecurityManager


Ozito