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.
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.
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.
Mientras que los elementos <method-permission>
especifican
qué Principal
s 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 identidadId1
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 identidadId2
.
4. El bean realiza las llamadas del método X con la identidadId2
.