En mi anterior post hablaba sobre el funcionamiento de las cookies en los navegadores y de cómo podíamos usar una extensión estándar (HttpOnly) para intentar impedir que las cookies sean accesibles desde el lado cliente y sólo se puedan manejar desde el servidor.
No obstante se trata de una medida que, de funcionar, complica un poco a los posibles piratas, pero no es una verdadera barrera de seguridad. Para empezar ni siquiera está soportada por todos los navegadores. Las cookies residen en el disco duro del usuario por lo que son fáciles de manipular por cualquiera. Además se envían al servidor en cada petición, por lo que cualquiera con un proxy estilo Fiddler puede leerlas y manipularlas antes de enviarlas al servidor.
Es decir, las cookies son elementos realmente inseguros.
A raíz de estas disquisiciones alguien me escribió preguntándome por la seguridad de la autenticación Forms de ASP.NET (la más habitual). Ésta se basa precisamente en la existencia de una cookie que identifica al usuario y que, por defecto, tiene una validez de 30 minutos, con renovación automática de este periodo en cada petición.
Un momento, ¿cómo?, ¿que la seguridad se basa en que hay una cookie en mi equipo? ¡Qué miedo!
Bueno esto es lo que podría pensarse si las cosas fueran tan sencillas como aparentan. Quiero decir que, en efecto, los datos sobre la sesión del usuario se almacenan en una cookie, pero no de cualquier manera, sino de manera segura.
El funcionamiento de este sistema es, básicamente, el siguiente:
1.- el usuario introduce sus credenciales y se validan (contra una base de datos o contra cualquier otro almacén marcado por el proveedor).
2.- Una vez validado positivamente se genera un ticket o testigo de la sesión actual con una validez de 30 minutos.
3.- Éste se encripta y se almacena en una cookie enviada al cliente y persistida durante la sesión del usuario.
4.- En cada nueva petición al servidor, la cookie se envía y un módulo especializado (FormsAuthenticationModule dentro del espacio de nombres System.Web.Security) desencripta la cookie y se encarga de re-autenticar al usuario en el sistema (estableciendo el objeto Principal actual y renovando la validez de la cookie si así está especificado).
Opcionalmente se puede almacenar el ticket en una cookie persistente que se guaradará en el disco duro del usuario durante por defecto 30 minutos (si bien se puede poner un valor algo más largo que generalmente será más apropiado). Eso significa que aunque el usuario cierre el navegador, al volver a abirlo se mantendrá su estado de autenticación intacto más allá de los 30 minutos por defecto y aunque cierre el navegador.
¿Qué se almacena en la cookie?
La cookie contiene el ticket de la autenticación Forms. Este ticket, representado por la clase FormsAuthenticationTicket, contiene los siguientes datos/miembros:
· Version: la versión del formato del ticket que se está usando. Actualmente la versión 2.
· Name: el nombre del usuario actual, que es único para todo el sistema y que es la pieza clave para luego restituir la sesión autenticada, así como ligarlo con otras API de ASP.NET como Roles o Profile.
· Expiration: cuándo caduca el ticket (y por lo tanto la cookie).
· IssueDate: fecha en la que se generó
· IsPersistent: si la cookie se guardará a disco o no.
· UserData: datos extra sobre el usuario. Normalmente esto es una cadena vacía ya que se escribe desde el proveedor de Membership, y las implementaciones por defecto no escriben nada aquí.
· CookiePath: la ruta relativa a partir la cual se almacena la cookie. Por defecto es "/".
Esta información es la que se serializa y se encripta, estableciendo una cookie para almacenarla en el cliente. Existe un método privado de la clase FormsAuthentication llamado MakeTicketIntoBinaryBlob que se encarga de serializar estos datos, que es llamado desde otro método privado, Encrypt, que se encarga del cifrado.
¿Cómo se encripta la cookie?
Dentro de la configuración de las cookies de autenticación en web.config, en el nodo <forms> podemos establecer algunas propiedades para controlar el comportamiento de este tipo de autenticación. Una de estas propiedades es protection. Puede tomar los valores siguientes:
· Encryption: con este valor el ticket se encripta antes de meterlo en la cookie.
· Validation: fuerza la validación de la cookie, pero no la encripta.
· All: Es el valor predeterminado y también el recomendado. Fuerza tanto el cifrado como la validación de la cookie que contiene el ticket de autenticación.
· None: hace que la cookie no se cifre ni se valide. Está para casos extremos pero se desaconseja totalmente su uso, ya que entonces sí que los temores que llevaron a crear este post serían ciertos, por que no hay protección alguna de la cookie. Eso sí, el rendimiento es mejor porque no tiene que hacer nada.
El cifrado se realiza usando la información especificada en la sección <machineKey> de web.config. A partir de la versión 2.0 de .NET por defecto se utiliza el algoritmo AES (Advanced Encryption Standard, el famoso Rijndael, estándar de la máxima seguridad en cifrado simétrico), pero también están soportados otros algoritmos menos seguros como DES y 3DES.
Para la validación de la cookie se emplea un algortimo de resumen digital (hash). Se concatenan los datos del ticket con una clave de validación, y se obtiene el hash del total usando los algoritmos SHA1 o MD5. POr defecto se usa SHA1, que es el más seguro y rápido de los dos. Dado que se le concatena una clave que sólo es conocida en el servidor, este esquema asegura que no se pueda añadir el código de validación de la cookie desde fuera.
La clave de cifrado y descifrado así como la de validación usadas por defecto es autogenerada en el servidor. Si pretendemos usar la autenticación (así como la validación del Viewstate y otros servicios dependientes) en una granja de servidores, deberemos generar estas claves manualmente y colocarlas en todos los servidores de la granja. Puedes leer todos los detalles aquí.
Como vemos se trata de un sistema muy seguro y que nos protege de intercepciones de la información de autenticación.
De hecho la mayor parte de los sistemas de autenticación de sitios con millones de usuarios como Facebook o Live ID de Microsoft, se basan en técnicas similares.
Analizando el código de las clases de seguridad Web de ASP.NET usando .NET Reflector se pueden ver con todo detalle los entresijos del funcionamiento interno de este sistema, algo que te recomiendo si estás interesado en todas estas cuestiones.
Existe un artículo de la Knowledge Base de Microsoft orientado a resolver problemas con la autenticación Forms que, en su último apartado, trae el código fuente de un módulo que se puede instalar en cualquiera de nuestras aplicaciones para descifrar en el servidor la información de cookies de autenticación que llega y poder hacer logging de la misma. Es un interesante código para analizar y aprender con él sobre el funcionamiento del sistema. Lo tienes aquí.
¡Espero que te sea útil! :-)