Seguridad en las webs (JSPs)

Resolviendo la manera de identificarse y desconectarse de forma adecuada y elegante

La mayoría de las webs no dedican grandes apartados a tratar de foma segura la información personal y/o confidencial que a través de ella se envía, como numeros de cuentas o tarjetas de credito (que es el que más nos afecta). Otro ejemplo es el uso de ordenadores compartidos, donde un usuario que accede posteriormente puede ver datos del que estuvo anteriormente sin mayor complicación, lo cual, aunque parezca mentira, ocurre en la mayoría de las paginas de bancos españoles que hemos probado.

Ejemplos similares ocurren al acceder desde sitio publicos como bibliotecas, o la propia facultad. Y es en este tipo de lugares donde se debe prestar mayor atención a la protección de datos, asumiendo además que el usuario no tiene por que tener ningún tipo de entrenamiento o conocimientos especiales sobre seguridad para que se le pueda garantizar que lo es.

Luego una aplicacion web perfecta, en cuanto a la proteccion de datos, debería comportarse de la siguiente manera: El usuario escribe la dirección de la página, y lo primero que se le muestra es una página para identificarse con sus credenciales, y asumiendo que estos son correctos la aplicacion web le permitiría acceder a todas las funcionalidades propias de su nivel de autorización. Cuando el usuario termina debe poder apretar un botón para proceder a su desconexión del sistema, y a partir de ahí lo único que se debería mostrar a cualquier usuario en cualquier condición sería la pagina de identificacion anterior. Hay además que asumir que el usuario puede olvidar presionar este botón, o simplemente cerrar el explorardor web. Además un usuario posterior con malas intenciones podría intentar ver el contenido de la página, que ya se encuentra en la cache del ordenador presionando el boton de 'Atrás' en el navegador

Una vez vista como debería comportarse la aplicación desde el lado del usuario, veamos como se pueden llevar a cabo esos distintos requisitos desde el lado del desarrollador. En primer lugar hay dos métodos ampliamente diferenciados para llevar a cabo todos esos puntos, la seguridad del contenedor o la personalizada. La primera hace referencia a la que ofrece un servidor de aplicaciones web ( en nuestro caso Tomcat 5.5), y la segunda a la que el desarrollador puede crear con cualquier otra herramienta. La primera ofrece rapidez y robustez, pero carece de la flexibilidad de la segunda, que es la que hemos decidido desarrollar.

La practica más común, y la que desarrollamos nosotros, es la de ofrecer al usuario un formulario para enviar sus credenciales, que luego es comprobado contra un LDAP(lightweight directory access protocol), para entornos altamente seguros, o contra una base de datos relacional. Si los credenciales son validos la accion de 'login' almacenará en el objeto HttpSession cierta información que permitirá saber a la aplicacion por quien está siendo utilizada.

Prevenir el acceso no autorizado a páginas JSP con seguridad.

En nuestro caso ninguna página, excepto la de identificación, es mostrada a un usuario no identificado. Así que el proceso de identificación almacena en el objeto HttpSession los datos del usuario, y la acción asociada al botón de desconexión hace justamente lo contrario, los elimina. De esta forma toda página lo primero que hace es comprobar que hay un usuario válido en su sesión antes de hacer nada, y en caso contrario redirecciona al usuario al 'login'.

Hasta aquí la aplicación se comporta, según el supuesto esperado, de forma correcta a la hora de mostrar información dinámica. Pero sin embargo no se comporta correctamente si despues de el usuario haberse desconectado alguien presiona el botón 'Atrás' del navegador, para ver las páginas anteriores, lo que indica que necesitamos algo más.

Evitar que el navegador almacene las páginas

El problema es que la mayoría de navegadores modernos tienen la opcion de apretar el botón 'Atrás', y simplemente muestran la información que tenian de antes, no recargan la página. Para evitar esto necesitamos decirle al navegador que no almacene nuestras páginas en la cache, lo cual es algo bueno en cuanto a seguridad, pero malo en cuanto al rendimiento a la hora de solicitar varias veces la misma página. Para esta gestión disponemos de las cabeceras HTTP 'Expires' y 'Cache-Control'. El HTTP Expires le dice al navegador cuando tardará esa página en caducar. El HTTP Cache-Control contiene los atributos para evitar el almacenanmiento en caché, y así cuando el botón atrás es pulsado se hace una nueva petición de la página a la aplicacion servidora. Los descriptores necesarios para esto son:

  • no-cahe: fuerza a obtener nuevas copias de la pagina
  • no-store: no almacena la página en el ordenador cliente

Así hemos conseguido que tras pulsar el botón de desconexión cualquier pulsación del botón atrás, no muestre en el navegador las páginas seguras, y redirija al usuario a la pagina de 'login'.

Aunque no obstante seguimos teniendo problemas para llegar a nuestro ejemplo perfecto. Si el usuario presiona el botón atrás en una página que envíaba informacion como resultado de una acción "POST", como por ejemplo la accion de identificarse en la aplicacion, los navegadores modernos mostraran un mensaje de este estilo:

  • IE Explorer:
    • Warning: Page has Expired. The page you requested was created using information you submitted in a form. This page is no longer available. As a security precaution, Internet Explorer does not automatically resubmit your information for you. To resubmit your information and view this Webpage, click the Refresh button.
  • Mozilla y FireFox:
    • The page you are trying to view contains POSTDATA that has expired from cache. If you resend the data, any action from the form carried out (such as a search or online purchase) will be repeated. To resend the data, click OK. Otherwise, click Cancel.

Y seleccionando esta opción reenviamos la información, lo cual conduciría, si esa informacion es la del usuario, a volver a estar autenticado en esa página, y evidentemente esa no es la finalidad del boton de desconexion. Por lo que necesitamos algo más.

Almacenamiento de la hora del ultimo registro

Para resolver este problema a la hora de identificarse ademas de la informacion con los credenciales del usuario se envía un campo obulto con la hora (milisegundos pasados desde 1970) que es inicializado dinámicamente. Así cuando la accion de validar el usuario es llamada, lo primero que hace es comarar el valor de este campo con el ultimo almacenado en la información del usuario en la base de datos. Y solo cuando el valor del formulario es estrictamente mayor que el almacenado en la base de datos se considera una sesion aceptable (cuando fuera producto de una recarga, ambos valores serían iguales).

Evidentemente para una autorización válida el campo correspondiente de cada usuario debe ser actualizado en la base de datos con el nuevo cuando la autorización se efectua.

Con estas tres medidas ya hemos conseguido un comportamiento según lo esperado en el ejemplo inicial.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License