martes, 8 de mayo de 2012

Cómo encriptar las contraseñas en Tomcat 7

Las contraseñas de los pools de conexiones a base de datos y del KeyStore de certificados (cuando se usa SSL) se almacenan en archivos XML de configuración en texto claro, por defecto. Nosotros no tenemos mayor problema con esto, ya que de todos modos hay que asegurar la instalación para restringir el acceso al servidor, pero es normal que en ambientes corporativos, como Bancos o Casas de Seguros esto no esté permitido, les representa un hallazgo de seguridad y piden que estas contraseñas sean almacenadas en forma encriptada, con algún algoritmo simétrico, como TripleDES con SHA1.

Si bien esto no le añade mucha más seguridad necesariamente, porque siempre se necesita otra llave para encriptar, es normal que los clientes soliciten este tipo de cambios, algo que los desarrolladores de Tomcat no parecen entender, por lo que pude ver en los forums, encriptar estas contraseñas ha sido una petición frecuente, pero que es despreciada por ellos con el argumento de que no ofrece mayor seguridad, etc, etc, en una mezcla de arrogancia y falta de pragmatismo.

Nosotros proveemos a los suscriptores de nuestra tecnología (Dinámica Deluxe) y a nuestros clientes un JAR que le añade a Tomcat 7 la capacidad de manejar las contraseñas de los pools de BD y de Keystore en forma encriptada. El JAR se llama dinamica-tomcat.jar y contiene unas clases que reemplazan las que vienen por defecto en Tomcat y que se ocupan de leer esas contraseñas. No fue un trabajo fácil, aunque debería serlo, porque Tomcat en su diseño no provee vías muy directas para afectar esta parte y proveer un lector de contraseña encriptada, sobre todo en el caso del KeyStore.

En el caso de los pools de conexiones a base de datos es un poco más fácil, requiere solo de una clase que extiende a BasicDataSourceFactory, luego esta clase es configurada en el archivo context.xml correspondiente, usando el atributo "factory".

En el caso del KeyStore, hay que crear una clase una extensión de la clase org.apache.tomcat.util.net.SSLImplementation, la cual será luego utilizada para configurar el conector SSL usando el atributo sslImplementationName. Para ser justos con el diseño de Tomcat, si bien pudo ser más fácil, al menos existe la forma de hacerlo vía extensión y configuración. El problema en el caso del KeyStore es que se requieren 2 clases más para lograr el efecto, básicamente tuvimos que identificar las clases, copiarlas de los fuentes de Tomcat a otro paquete y hacerle los ajustes para leer el password encriptado, que van en un método específico.

El JAR que proveemos contiene unos comandos para generar la contraseña encriptada, y también para codificar la llave que se usa para encriptar y desencriptar. Por ejemplo para generar la contraseña encriptada teniendo ya la llave codificada, se usa este comando:

@java -classpath ./dinamica-tomcat.jar -Dsemilla=cGVwaXRv dinamica.tomcat.MakePassword basica
En este caso la contraseña basica queda transformada en: A31CInaRgDk=

Así queda la configuración de un pool de conexiones a base de datos con el uso de contraseña encriptada y codificada en Base64:


El atributo factory indica que vamos a usar nuestra clase para leer la configuración, en particular el atributo password.

Para el caso del KeyStore se modifica server.xml para configurar el conector SSL de esta manera:


El atributo sslImplementation le dice a Tomcat 7 que use nuestra clase, la cual permite leer el atributo password encriptado.

De esta manera proveemos a nuestros clientes una forma sencilla para resolver el problema de las contraseñas en texto claro, sin configuraciones rebuscadas y suficiente para pasar una auditoria de seguridad.

Saludos,
el Team Dinámica

miércoles, 18 de enero de 2012

De terror: Weblogic y filtros de servlets

Para hacer el cuento corto: a un suscriptor de Dinámica en Perú le tocó instalar una webapp hecha con nuestro framework en un servidor WebLogic y reportó un comportamiento absolutamente anormal: "un usuario sin autorización puede ejecutar un URL restringido, el filtro de seguridad de Dinámica parece no estar funcionando en WebLogic! aunque las pruebas en Tomcat funcionaron perfecto..." y por nuestra parte hemos verificado este mecanismo en Glassfish, Oracle J2EE container y Resin, además de Tomcat. Tiene años funcionando sin novedades.

No tuvimos alternativa, nos tocó montar un WebLogic de desarrollo para hacer nuestras propias pruebas controladas, usamos el último disponible, v12.1.1. Un proceso innecesariamente accidentado, a pesar que en principio era descomprimir un ZIP y cambiar unas variables de ambiente, resultó un dolor de cabeza por diversos motivos. En fin, lo pusimos en marcha y procedimos a probarlo reproduciendo el caso reportado por nuestro suscriptor, usando la webapp que sirve como consola de seguridad -Admin-, pero levantando unas trazas adicionales que serían útiles para diagnosticar el caso, usando tanto facilidades del framework Dinámica así como los logs del servidor.

Usando una credencial de usuario sin suficientes permisos, pudimos comprobar que nos dejaba ejecutar URLs restringidas... ese era el síntoma. Las evidencias resultaron más inquietantes.

El filtro de seguridad verifica los roles del usuario y los niveles de autorización de cada rol para ver si el URL requerido puede o no ser ejecutado, si no tiene permisos suficientes, se retorna un error http 403, acceso denegado, de lo contrario se deja pasar el request. No hay espacio para la ambiguedad:

Cuando verificamos los logs HTTP de WebLogic, la cosa se pone peor:

[12/Jan/2012:21:04:26 -0430] "GET /admin/action/admin/app/view HTTP/1.1" 200 4648 
[12/Jan/2012:21:06:12 -0430] "GET /admin/action/admin/app/edit?id=501 HTTP/1.1" 403 1833


El URL o Action -en la jerga de Dinámica- en cuestión es /app/edit, pudimos constatar en el browser que el servidor nos retornó la página que por la configuración de seguridad no deberíamos haber visto, pero lo más extraño es que quedó registrada una respuesta 403, de acceso denegado, sin embargo la página objetivo fue retornada, no un mensaje de error, ¿cómo es esto posible? El filtro parece estar haciendo su trabajo, pero el mismo es ignorado por WebLogic!

Más evidencia, esta vez de las trazas que deja el mecanismo de seguridad de Dinámica al momento de interceptar el request para el URL restringido /app/edit:

#### <[Dinamica_DEBUG_SecurityFilter] URI (/action/admin/app/edit) Intercepting request...> 
#### <[Dinamica_DEBUG_SecurityFilter] URI (/action/admin/app/edit) Session Cookie: f78WPPKWfgbGCNjhgJhTv80GpqqvWjXnWXyh19Nb0QlxfQQcNp1W!-750755119!1326418454955>
#### <[Dinamica_DEBUG_SecurityFilter] URI (/action/admin/app/edit) Authorized Roles (sysadmin;) USER (mcordova) User Roles (tester;) > 
#### <[Dinamica_DEBUG_SecurityFilter] URI (/action/admin/app/edit) Request rechazado por falla de autorización> 

Se puede ver claramente que el filtro rechazó el request!!! nunca se ejecutó el método next.doFilter() que es  lo que haría que el request se ejecute después de pasar por el filtro. ¿Cómo es posible que WebLogic retorne la página si nunca se ejecutó el doFilter? Una falla horrorosa en la implementación de la especificación de servlets.

La causa:

Resulta ser que en el web.xml de una aplicación hecha por Dinámica, se mapea el código de error HTTP 403 a un Servlet, práctica perfectamente aceptada en el estándar de servlets.


Pues esto no le gustó a WebLogic, y en vez de dejar algún rastro en sus logs o de retornar un propio mensaje para el código 403, decidió que era mejor retornar el recurso solicitado, aunque el filtro nunca lo dejo pasar!

La solución al problema fue usar un recurso estático, una página HTML en la raíz de la webapp y referenciarlo como mapping para el error 403 en web.xml. El problema desapareció y el filtro de seguridad de Dinámica funcionó como se esperaba, consistente con la configuración de seguridad.

La experiencia sirvió para enriquecer y actualizar nuestra guía de Deployment JEE de Dinámica, que muestra como hacer deployment de webapps hechas con Dinámica en distintos servidores JEE, ahora incluyendo a WebLogic v12.1.