Java realiza un importante control de seguridad en los programas, que impide que programas escritos en dicho lenguaje puedan dañar nuestra información (virus), accedan a información privada, o realicen cualquier otra actividad dañina.
En principio, en JDK 1.0 Java únicamente lo que hacía era aplicar una serie de restricciones de seguridad sobre el código remoto que se ejecutase en nuestra máquina (Applets). Proporcionaba a estos programas un acceso restringido a los recursos de nuestro sistema.
En JDK 1.1 se incorporan los llamados Applets firmados. En los Applets que tengan una firma de confianza, se relajarán las restricciones de seguridad, según las necesidades del Applet en cuestión. Esto permitirá incorporar más capacidades a los Applets, que podrán ser utilizadas siempre que el usuario confíe en la firma y acepte otorgar al Applet los permisos que solicita.
En JDK 1.2 se producen grandes cambios en el sistema de seguridad. Ahora ya no sólo se aplican las restricciones de seguridad a código remoto, sino que pueden ser aplicadas a cualquier programa Java, ya sea local o remoto. Las clases Java se organizan en una serie de dominios, de forma que cada dominio tiene una serie de permisos propios. Uno de estos dominios serán los Applets, que tendrán una serie de restricciones de seguridad como en versiones anteriores. Las aplicaciones por defecto no tienen restricciones, pero podemos ponérselas.
Los Applets son programas Java que pueden encontrarse incrustados en la web, de forma que cuando entremos en la página el código del Applet se descargué en nuestra máquina y se ejecute automáticamente. Si dejásemos que cualquiera de estos programas accediese a todos los recursos podría ser muy peligroso, ya que cualquiera simplemente colgando un Applet en su página podría tener libertad para hacer casi cualquier cosa dentro de los ordenadores de todo aquel que visite la web. Por esto los Applets cuentan con una serie de restricciones importantes:
Podremos cambiar la configuración de forma que se eliminen algunas de estas restricciones, estableciendo los permisos necesarios para ello. De la misma forma, aunque las aplicaciones no tengan por defecto estas restricciones de seguridad, podremos hacer que también se vean sujetas a ellas.
Los ficheros de política (policy) establecen la políticas de seguridad (permisos) que se llevarán a cabo en los programas Java que utilicemos. Estos ficheros son ficheros de texto que pueden ser editados con cualquier editor ASCII o bien con la herramienta incluida en la distribución de JDK para tal fin: Policy Tool.
Para ejecutar la aplicación Policy Tool deberemos introducir en la línea de comando:
policytool
Hecho esto se mostrará la aplicación, que intentará buscar el fichero de políticas por defecto (.java.policy) en el directorio HOME del usuario actual (o en el directorio donde esté instalado el S.O. en caso de que no sea multiusuario). Si no localiza dicho fichero la aplicación aparecerá en blanco:
Para otorgar permisos deberemos Agregar entrada de norma. Al pulsar este botón se nos mostrará la siguiente ventana:
En esta ventana deberemos especificar a qué programas afectará la norma creada. Si no se especifica nada, la norma afectará a cualquier programa. Podemos restringir el número de programas afectados por la norma especificando:
Una vez definido el grupo de programas afectado por la norma, podemos pasar a agregar los permisos que deseemos otorgar a dichos programas. Para ello pulsamos en Agregar permiso, tras lo cual aparecerá la siguiente ventana:
En el menú de permisos deberemos seleccionar el tipo de permiso que queramos otorgar. Según el tipo de permiso seleccionado podremos o no dar valor al resto de campos, que se referirán a datos sobre el permiso seleccionado.
Una vez introducidos los datos, pulsamos Aceptar para que el permiso sea añadido a la norma. Cuando hayamos terminado de añadir permisos a la norma pulsamos Terminar y veremos la norma en la lista de normas. Ahora podemos grabar el fichero de políticas desde el menú Archivo, mediante la opción Guardar como.
Cuando se ejecuta una aplicación o Applet con un manejador de seguridad, los ficheros de políticas que se cargan son los definidos en el fichero de propiedades de seguridad que se encuentra en el directorio {java.home}/lib/security/java.security.
Para utilizar el fichero de políticas que hayamos definido, podemos optar bien por añadirlo a este fichero de propiedades de seguridad, o bien añadirlo como propiedad del sistema el ejecutar el intérprete Java, utilizando para ello el siguiente parámetro:
appletviewer -J-Djava.security.policy=mipolitica MiApplet
De esta manera estamos forzando a que se utilice la política definida en el fichero mipolitica.
En una aplicación Java por defecto no se instala ningún gestor de seguridad. Si queremos imponer restricciones de seguridad podemos forzar que se cargue un gestor de seguridad por defecto llamando al interprete con el parámetro:
java -Djava.security.manager MiAplicacion
Podemos además especificar el fichero de políticas que queremos que utilice el gestor de seguridad cargado, esto lo haremos de la siguiente forma:
java -Djava.security.manager -Djava.security.policy=mipolitica MiAplicacion
El gestor de seguridad (SecurityManager) será el objeto encargado de determinar si cierta operación es permitida o no, impidiendo su realización en tal caso. Cuando una aplicación carga un gestor de seguridad todas las acciones que vaya a realizar sujetas a posibles restricciones de seguridad se comprobarán en dicho gestor antes de ser realizadas.
En el caso de los Applets es el navegador el encargado de instalar en ellos el gestor de seguridad por defecto, por lo que siempre estarán sujetos a restricciones de seguridad.
En las aplicaciones independientes no se carga por defecto ningún gestor de seguridad, pero podremos hacer que se cargue bien indicándolo en la línea de comando como hemos visto en el punto anterior, o bien desde el mismo código de nuestra aplicación.
El gestor de seguridad por defecto (clase SecurityManager) será el gestor que carguen los Applets, o las aplicaciones cuando lo especifiquemos en la línea de comando. Este gestor de seguridad seguirá la política de seguridad indicada en los ficheros de políticas que hayamos indicado (tanto en el fichero de propiedades de seguridad como en la línea de comando).
Además de poder utilizar el gestor de seguridad definido por defecto, también podremos definir nuestro propio gestor de seguridad creando una subclase de SecurityManager y sobrescribiendo en ella una serie de métodos para darles el comportamiento que queramos.
Esta clase tiene una serie de métodos de nombre checkXXXX donde XXXX se refiere el tipo de operación de la que comprueban el permiso. Por ejemplo, checkRead(String filename) comprobará si se permite la lectura de un determinado fichero, dado su nombre. Estos métodos comprobarán si la operación en cuestión está permitida. En caso de estar permitida, el método simplemente devolverá el control al llamador y se continuará la ejecución de forma normal, mientras que si no se permite la operación, lo que hará será lanzar una excepción SecurityException, lo cual interrumpirá el flujo normal del programa.
Por lo tanto, cuando se vaya a realizar una acción susceptible de no tener permiso, lo que hará el método que realice dicha operación será previamente comprobar si hay algún manejador de seguridad instalado, y de ser así llamará al método checkXXXX asociado a la operación que vaya a realizar. Si está permitida nos devolverá el control de forma normal podremos realizar la operación, mientras que si no lo está se producirá una excepción interrumpiéndose así el flujo normal del programa e impidiéndose por lo tanto la realización de la operación.
La implementación de la operación de salida, por ejemplo, será como la siguiente:
public void exit(int status) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkExit(status); } // Este código sólo se ejecutará si checkExit() termina. // Aquí realizaremos la operación de salida de la aplicación. }
Vemos en el ejemplo que para obtener el gestor de seguridad instalado actualmente se utiliza el método getSecurityManager de la clase System.
Para escribir nuestro propio gestor de seguridad deberemos redefinir todos los métodos checkXXXX de los cuales queramos cambiar el comportamiento, añadiendo código que compruebe si se dan las condiciones para otorgar permiso, lanzando una excepción SecurityException en caso de no permitirse la acción.
Por ejemplo, si queremos permitir únicamente que una aplicación salga con código de salida 0, podríamos crear el siguiente gestor de seguridad propio.
class MiGestor extends SecurityManager { public void checkExit(int status) throws SecurityException { if(status != 0) throw new SecurityException(); } }
Una vez creado el gestor de seguridad, para que se haga efectivo deberemos instalarlo.
Para instalar un gestor de seguridad, de forma que sea utilizado para establecer las operaciones permitidas, utilizaremos el método setSecurityManager de la clase System. Por ejemplo, si queremos instalar el gestor de seguridad definido en el ejemplo anterior haremos:
MiGestor gestor = new MiGestor(); System.setSecurityManager(gestor);
Pero, ¿qué ocurriría si un método "malicioso" sustituyese el gestor de seguridad actual por uno que otorgue todos los permisos? Si esto ocurriese, simplemente instalando un manejador de seguridad propio cualquier programa podría saltarse las restricciones de seguridad y todo el sistema de seguridad de Java no serviría para nada. Para evitar este problema lo que ocurre es que si hay instalado un gestor de seguridad no se nos permitirá instalar otro.
Cualquier programa (aplicación o Applet) que se cargue con un gestor de seguridad instalado por defecto no permitirá que instalemos nuestro propio gestor dentro del código. De igual forma, si en una aplicación instalamos un gestor de seguridad en tiempo de ejecución (dentro del código), no podremos sustituirlo posteriormente por otro ni eliminarlo.