lunes, 25 de junio de 2007

Acerca de la autentificación (CADUCADO)

CADUCADO: La información de este post apesta por lo vieja. Es posible que ya no sea válida con las versiones más recientes de CakePHP. Se mantiene público  para vergüenza y escarnio del autor.


Después de varias semanas parece que va siendo hora de empezar a escribir código de verdad y no simples ejercicios y pruebas "a ver cómo va esto del Cake".

Por supuesto, para una aplicación seria tengo que buscar un sistema de autentificación y de gestión de permisos.

CakePHP 1.1 no incorpora ningún método propio de autentificación y sí un sistema de ACL (Access Control Lists) sobre la base de que:
  • No es difícil desarrollarlo usando el framework
  • Cada desarrollador tienen sus necesidades
CakePHP 1.2 tiene un componente Auth con muy buena pinta pero sin documentación. De hecho, todavía no he visto ningún tutorial que te permita entrar un poco en harina. Además, incorpora una nueva implementación de la ACL sobre base de datos que tiene una pinta excelente también, pero también poco documentada.

Aparte, existen unos cuantos sistemas de autentificación que son interesantes o tienen elementos interesantes:
El problema es que ninguno de ellos me acaba de convencer totalmente por una u otra razón.

Por lo pronto, habría que separar el sistema de autentificación del sistema de permisos. O al menos eso es lo que quiero hacer yo, y en alguno de los casos señalados hay cierta interacción entre ambos.

Quizá lo mejor sea coger lo que más me guste de cada uno y hacer algún refrito.

Autentificación

Lidiar con la autentificación supone intentar que:
  • Los usuarios del sistema con credenciales adecuadas puedan "entrar"
  • Evitar que usuarios maliciosos puedan lograr acceso o hacerse con contraseñas legítimas
Lo explica muy bien, Dieter Plaetnick en este artículo y en este otro. Plantea los problemas que hay que afrontar y resolver. Básicamente viene a decir:
  • Almacenar las contraseñas encriptadas para que no se puedan obtener en caso de acceso a la base de datos (cierto, si un atacante accede a la base de datos estás vendido de todos modos, pero Dieter se refiere, por ejemplo a los alojamientos compartidos, donde hay técnicos que podrían curiosear).
  • Yo añadiría que la aplicación no permita ver las contraseñas (ni siquiera los hash) a ningún usuario, ni siquiera administrador ni el mismo propietario. Si la contraseña se pierde, se cambia por otra, no es tan difícil.
  • Enviar las contraseñas encriptadas del navegador al servidor, bien sirviendo por https (http + ssl, de modo que toda la comunicación viaja encriptada) o bien encriptando con Javascript en el navegador.
  • Para dificultarlo más, utilizar una "sal" que convierta cada contraseña en única para la sesión. Esto es, que exista una especie de modificación de la contraseña para una sesión en particular, de modo que incluso en el supuesto caso de que un atacante intercepte la contraseña encriptada no pueda utilizarla en otra sesión. La cosa es generar una cadena (la sal) al iniciar una nueva sesión, incorporarla en el formulario (o en una cookie) del lado el cliente y en la sesión del para que se añada de algún modo a la contraseña (por ejemplo, encriptar la contraseña en el formulario y adjuntarle la "sal" y a lo mejor encriptar de nuevo). En el servidor, hacer algo parecido con la contraseña almacenada y comparar los hash resultantes.
  • Lidiar con los ataques de fuerza bruta ue prueban a gran velocidad miles de contraseñas hasta encontrar alguna válida. Una forma de resolver esto es limitando la cantidad de intentos que se pueden hacer en un tiempo determinado, estableciendo listas "negras" de IP bloqueadas.
  • Lidiar con la SQL injection, o sea, un intento de colar código SQL en los datos del formulario de login con el que se pueden conseguir cosas tan hermosas como login sin contraseña ni nada. Simplemente, un saneamiento de datos al recibir un formulario lo previene.
  • Yo añadiría establecer unas reglas de validación de las contraseñas que obliguen a los usuarios a elegir contraseñas sólidas, como por ejemplo, incluir caracteres en mayúscula, minúscula, números y algún signo de puntuación. Es molesto, pero más molesto es que te birlen las credenciales y hagan guarrindongadas con tu cuenta.
  • Otra historia es prevenir el posible "secuestro de sesión", es decir, que un usuario malicioso nos "sustituya" en medio de una sesión en la que ya estamos autentificados. Se puede hacer algo al respecto si guardamos suficiente información sobre la sesión actual, de modo que podamos detectar cosas como que haya cambiado la IP del cliente en plena sesión y cosas así.
Todo lo anterior es el planteamiento del problema. Luego hay que tener en cuenta todo el proceso de autentificación:
  • Si no hay usuarios conectados, presentar el login (o si el usuario activa un enlace para que le salga el login)
  • Mantener la información del usuario conectado para no pedirle el login cada dos por tres.
  • Si un usuario está conectado, que no le salga el login.
  • Si una acción está reservada a usuarios conectados, actuar en consecuencia. Es decir, que le pida el login si no está conectado ya o que no se lo pida si ya está dentro.
En cuanto a cómo montar esto en CakePHP parecen claras algunas cosas.

Debe haber un componente. Puesto que la acción se lleva a cabo en los controladores, es en éstos donde debemos introducir el código que se encarga de gestionar los usuarios conectados. La mejor forma es un componente.

Por otro lado, habría que definir modelos para usuario e intentos de login, (los grupos, en su momento). El modelo de usuario podría tener una información acerca del último login o del estado de conexión. O igual es mejor tenerlo en una tabla aparte. Algo así:

Users
id
username
password
email
login_desde (para controlar si agota el tiempo de sesión, hay que definir un tiempo de timeout)
remote_ip (para controlar si cambia en algún momento)

Intentos
id
remote_ip (para controlar la IP que está intentando acceder)
status (bloqueada, lista negra..., esto nos permitiría controlar que una determinada IP sea tratada de modo especial)

Tiene que haber, lógicamente controlador y modelos para administrar los usuarios y también alguna forma de revisar los intentos de conexión para poder hacer cosas como desconectar usuarios desde el sistem, o desbloquear/bloquear manualmente direcciones, mostrar información del usuario conectado.

Un helper para generar formularios de login con los scripts de encriptación tampoco vendría mal.

Sobre este sistema, se podría montar un sistema de perfiles que nos permita tener información y preferencias del usuario conectado, y de grupos para asignar permisos en bloque. Eso sí, el tema de permisos lo dejaré para otro rato. Por hoy ya me llega.

No hay comentarios: