Tema 8: Seguridad

8.1 Introducción a la seguridad en EJBs

Los servidores Enterprise JavaBeans pueden soportar tres tipos de seguridad: autentificación, comunicación segura y control de acceso. En el módulo de seguridad se ha hecho hincapié en los dos primeros tipos de servicios usando las APIs que proporciona Java. En este apartado veremos qué mecanismos define la especificación EJB para el control de acceso a los EJBs. Revisemos brevemente cada uno de los tipos de seguridad.

Autentificación

Dicho sencillamente, la autentificación valida la identidad del usuario. La forma más común de autentificación es una simple ventana de login que pide un nombre de usuario y una contraseña. Una vez que los ususarios han pasado a través del sistema de autentificación, pueden usar el sistema libremente, hasta el nivel que les permita el control de acceso. La autentificación se puede basar también en tarjetas de identificación, certificados y en otros tipos de identificación.

Comunicación segura

Los canales de comunicación entre un cliente y un servidor son un elemento muy importante en la seguridad del sistema. Un canal de comunicación puede hacerse seguro mediante aislamiento físico (por ejemplo, via una conexión de red dedicada) o por medio de la encriptación de la comunicación entre el cliente y el servidor. El aislamiento físico es caro, limita las posibilidades del sistema y es casi imposible en Internet, por lo que lo más usual es la encriptación. Cuando la comunicación se asegura mediante la encriptación, los mensajes se codifican de forma que no puedean ser leídos ni manipulados por individuos no autorizados. Esto se suele consigue mediante el intercambio de claves criptográficas entre el cliente y el servidor. Las claves permiten al receptor del mensaje decodificarlo y leerlo.

Control de acceso

El control de acceso (también conocido como autorización) aplica políticas de seguridad que regulan lo que un usuario específico puede y no puede hacer en el sistema. El control de acceso asegura que los usuarios accedan sólo a aquellos recursos a los que se les ha dado permiso. El control de acceso puede restringir el acceso de un usuario a subistemas, datos, y objetos de negocio. Por ejemplo, a algunos usuarios se les puede dar permiso de modificar información, mientras que otros sólo tienen permiso de visualizarla.

La mayoría de los servidores EJB soportan la comunicación segura a través del protocolo SSL (Secure Socket Layer) y proporcionan algún mecanismo de autentificación, pero la especificación Enterprise JavaBeans sólo especifica el control de acceso a los EJBs.

Aunque la autentificación no se especifica en EJB, a menudo se consigue usando el API JNDI. Un cliente que usa JNDI puede proporcionar información de autentificación usando este API para acceder a los recursos del servidor. Esta información se suele pasar cuando el cliente intenta iniciar una conexión JNDI con el servidor EJB. El siguiente código muestra cómo se añaden la contraseña y el nombre de usuario a las propiedades de la conexión que se usan para obtener una conexión JNDI con el servidor EJB:

properties.put(Context.SECURITY_PRINCIPAL, userName );
properties.put(Context.SECURITY_CREDENTIALS, userPassword);

javax.naming.Context jndiContext = new javax.naming.InitialContext(properties);
Object ref= jndiContext.lookup("AccountEJB");
AccountHome accountHome = (AccountHome)
    PortableRemoteObject.narrow(ref, AccountHomeRemote.class);

EJB especifica que todas las apliaciones clientes que acceden a un sistema EJB deben estar asociadas con una identidad de seguridad. La identidad de seguridad representa el cliente o bien como un usuario o bien como un rol. Un usuaro podría ser una persona, una credencial de seguridad, un computador o incluso una tarjeta inteligente. Normalmente, el usuario es una persona a la que se le asigna una identidad cuando entra en el sistema. Un rol especifica una categoría de acceso a la que pueden pertenecer distintos usuarios. Por ejemplo, el rol "ReadOnly" definiría una categoría en la que sólo se permite el acceso a operaciones de lectura de datos.

Normalmente el servidor EJB permite definir grupos de usarios a los que se les va a asignar las mismas restricciones de seguridad. ¿Cuál es la diferencia entre roles y grupos? La diferencia fundamental es que los roles son dependientes de la aplicación que se despliega, mientras que los grupos son dependientes de la organización. Después de desplegar una aplicación es necesario realizar una asignación de roles a usuarios o grupos de usuarios. Esta separación entre roles y usuarios permite hacer la aplicación portable e independiente de la organización en la que se despliega.

Cuando un cliente remoto se autentifica en el sistema EBJ, se le asocia una identidad de seguridad que dura el tiempo que está activa la sesión. Esta identidad se encuentra en una base de datos o directorio específico de la plataforma EJB. Esta base de datos o directorio es responsable de almacenar las indentidades individuales, su pertenencia a grupos y las asignaciones de roles a grupos e individuos.

Una vez que se le ha asociado una identidad de seguridad a un cliente remoto, está listo para usar los beans. El servidor EJB realiza un seguimiento de cada cliente y de su identidad. Cuando un cliente invoca un método de un EJB, el servidor EJB pasa implícitamente la identidad del cliente junto con la invocación al método. Cuando el objeto EJB o el EJB home recibe la invocación debe chequear la identidad para asegurarse de que ese cliente puede usar ese método.

8.2 Control de acceso basado en roles

En Enterprise JavaBeans, la identidad de seguridad se representa por un objeto java.security.Principal. Este objeto actúa como representante de usuarios, grupos, organizaciones, tarjetas inteligentes, etc. frente a la arquitectura de control de acceso de los EJB.

Los descriptores del despliegue incluyen elementos que declaran a qué roles lógicos se permite el acceso a los métodos de los enterprise beans. Estos roles se consideran lógicos porque no reflejan directamente usuarios, grupos o cualquier otra identidad de seguridad en un entorno operacional específico. Los roles se hacen corresponder con grupos de usuarios o usuarios del mundo real cuando el bean se despliega. Esto permite que el bean sea portable ya que cada vez que el bean se despliega en un nuevo sistema, los roles se asignan a usuarios y grupos específicos de ese entorno operacional.

He aquí una porción de un fichero de despliegue de un EJB que define dos roles de seguridad, ReadOnly y Administrator:

<security-role>
    <description>
       A este rol se le permite ejecutar cualquier método del 
       bean y leer y escribir cualquier dato del bean.
    </description>
    <role-name>
        Administrator
    </role-name>
</security-role>

<security-role>
    <description>
        A este rol se le permite localizar y leer información del bean.
        A este rol no se le permite modificar los datos del bean.
    </description>
    <role-name>
        ReadOnly
    </role-name>
</security-role>

Los nombres de los roles en este descriptor no son nombres reservados o especiales con significados predefinidos; son simplemente nombres lógicos escogidos por el ensamblador del bean.

Una vez que se declaran los roles, pueden asociarse con métodos en los beans. El elemento <method-permission> es en el que definimos a qué métodos puede acceder cada uno de los roles.

<method-permission>
    <role-name>Administrator</role-name>
    <method>
        <ejb-name>CustomerEJB</ejb-name>
        <method-name>*</method-name>
    </method>
</method-permission>
<method-permission>
    <role-name>ReadOnly</role-name>
    <method>
        <ejb-name>CustomerEJB</ejb-name>
        <method-name>getName</method-name>
    </method>
    <method>
        <ejb-name>CustomerEJB</ejb-name>
        <method-name>getAddress</method-name>
    </method>
    <method>
        <ejb-name>CustomerEJB</ejb-name>
        <method-name>findByPrimaryKey</method-name>
    </method>
</method-permission>

En el primer <method-permission> el rol Administrator se asocia con todos los métodos del EJB CustomerEJB. En el segundo, se limita el acceso del rol ReadOnly sólo a tres métodos: getName(), getAddress() y findByPrimaryKey(). Cualquier intento de un rol ReadOnly de acceder a un método que no sea éstos resultará en una excepción.

Al desplegar el bean debemos examinar el bean y asignar cada rol lógico a cada grupo de usuarios en el entorno operacional, sin preocuparnos de qué métodos son accesibles por cada rol. Esto muestra una vez más las ventajas de la separación de papeles que promueve la arquitectura EJB. El desarrollador del bean es el que debe de preocuparse de definir los roles y restringir el acceso a los métodos, mientras que la persona que hace el despliegue se ocupa de asignar los roles a usuarios y grupos.

Una vez que el bean se despliega, el contenedor se encarga de comprobar que los usuarios acceden únicamente a aquellos métodos a los que tienen permisos. Esto se consigue propagando la identidad de seguridad, el objeto Principal, en cada invocación del cliente al bean. Cuando el cliente invoca un método de un bean, el objeto Principal del cliente se chequea para comprobar si el rol asociado tiene los privilegios necesarios.

Si un bean intenta acceder a cualquier otro bean mientras que está sirviendo a un cliente, pasará al segundo bean la identidad del cliente para que se realiza un control de acceso en este segundo bean. De esta manera, el Principal del cliente se propaga de un bean al siguiente, asegurando que se controla el acceso acceda o no directamente al EJB.

8.3 Métodos no-chequeados

En EJB 2.0 un conjunto de métodos se puede designar como unchecked, que significa que los permisos de seguridad no se chequean. Un método no chequeado puede invocarse por cualquier cliente, sin importancia de el rol que ese cliente esté usando. A continuación hay un ejemplo de declaración de métodos no-chequeados:

<method-permission>
    <unchecked/>
    <method>
        <ejb-name>CabinEJB</ejb-name>
        <method-name>*</method-name>
    </method>
    <method>
        <ejb-name>CustomerEJB</ejb-name>
        <method-name>findByPrimaryKey</method-name>
    </method>
</method-permission>
<method-permission>
    <role-name>administrator</role-name>
    <method>
        <ejb-name>CabinEJB</ejb-name>
        <method-name>*</method-name>
    </method>
</method-permission>

Esta declaración nos dice que todos los métodos del EJB Cabin, junto con el método findByPrimaryKey() del EJB Customer, son no-chequeados. Un segundo elemento le da al administrador permiso para acceder a todos los métodos del EJB Cabin. El permiso no-chequeado siempre tiene prioridad sobre cualquier otro permiso.

8.4 La identidad de seguridad runAs

Mientras que los elementos <method-permission> especifican qué Principals tienen acceso a qué métodos del bean, el elemento <security-identity> especifica bajo qué Principal el método va a ejecutarse. En otras palabras, el objeto Principal runAs se usa como la identidad del enterprise bean cuando éste intenta invocar métodos en otros beans. Esta identidad no tiene por qué ser la misma que la identidad que accede al bean por primera vez.

Por ejemplo, los siguientes elementos del descriptor de despliegue declaran que el método create() sólo puede accederse por el rol JimSmith y que el EJB Cabin siempre se va ejecutar con una identidad Administrator.

<enterprise-beans>
...
    <entity>
        <ejb-name>CabinEJB</ejb-name>
        ...
        <security-identity>
            <run-as>
               <role-name>Administrator</role-name>
            </run-as>
        </security-identity>
        ...
    </entity>
...
</enterprise-beans>
<assembly-descriptor>
<security-role>
    <role-name>Administrator</role-name>
</security-role>
<security-role>
    <role-name>JimSmith</role-name>
</security-role>
...
<method-permission>
    <role-name>JimSmith</role-name>
    <method>
        <ejb-name>CabinEJB</ejb-name>
        <method-name>create</method-name>
    </method>
</method-permission>
...
</assembly-descriptor>

Esta clase de configuración es útil cuando el enterprise bean o los recursos accedidos en el cuerpo del método requieren un Principal distinto del que ha sido usado para obtener acceso al método. Por ejemplo, el método create() podría llamar a un método en el enterprise bean X que requiera la identidad de seguridad de Administrador. Si quisiéramos usar el enterprise bean X en el método create(), pero sólo permitimos a Jim Smith la creación de nuevos camarotes, usaríamos conjuntamente los elementos <method-permission> y <security-identity> para conseguirlo. El <method-permission> para create() especificaría que sólo Jim Smith puede invocar el método, y el elemento <security-identity> especificaría que el enterprise bean siempre se debe ejecutar bajo la identidad de seguridad Administrator. Para especificar que un enterprise bean debe ejecutarse bajo la identidad de quien hace la llamada, el rol <security-identity> contiene un único elemento vacío, el elemento <use-caller-identity>. Por ejemplo las siguientes declaraciones especifican que el EJB Cabin se ejecute siempre bajo la identidad de quien hace la llamada, por lo que si Jim Smith invoca el método create(), el bean se ejecutará bajo la identidad de seguridad JimSmith:

<enterprise-beans>
...
    <entity>
        <ejb-name>CabinEJB</ejb-name>
        ...
        <security-identity>
            <use-caller-identity/>
        </security-identity>
        ...
    </entity>
...
</enterprise-beans>

Podemos entender la secuencia de cambio de identidades de la siguiente forma:

1. El cliente invoca el método X del bean con una identidad Id1.
2. El bean comprueba si la identidad Id1 tiene permiso para ejecutar el método X. La tiene.
3. El bean consulta el elemento <security-identity> y cambia la identidad a la que indica ese elemento. Supongamos que es la identidad Id2.
4. El bean realiza las llamadas del método X con la identidad Id2.