En un apunte anterior de la bitácora explicaba como gestionar contraseñas seguras en PHP. Pero, qué sucede si por inexperiencia empezamos a guardar las contraseñas en plano y queremos migrar a un sistema seguro? O aún más complicado, y si empezamos a usar un sistema tipo hash que pensábamos que era seguro como sha1 pero que se ha demostrado que no lo es? Veamos los dos casos por separado.
Pasar de contraseñas en plano a cifradas
Este es el caso más fácil de solventar ya que simplemente tendremos que generar el hash con salt de todas las contraseñas y empezar a utilizarlo. Ahora bien, como no queremos que nuestro sistema en producción se detenga podemos hacerlo de la siguiente manera.
- Añadimos una nueva columna a nuestra tabla en base de datos en caso de utilizar un sistema de gestión de base de datos relacional o un dato nuevo dependiendo del sistema que utilicemos.
- Generaremos la nueva contraseña cifrada para cada usuario y la almacenaremos en la nueva columna.
- Cambiaremos nuestro código para que cuando se cree un nuevo usuario la contraseña se genere y guarde cifrada, lo mismo en el caso de que se permita el cambio de contraseña. También que cuando se autentique un usuario se compruebe con el nuevo sistema. Esto lo podremos hacer porque tendremos la columna con la contraseña nueva cifrada y segura.
- Una vez testeado que todo funciona subiremos este nuevo código a producción.
- Ahora ya podemos hacer un update en la base de datos de forma que machaquemos las contraseñas en plano por las cifradas, sería algo así:
update usuario set passwd = new_passwd;
En este punto ya no tendremos contraseñas en plano sino que todo estará cifrado. Ya nos podemos olvidar de problemas de seguridad derivados de las contraseñas en plano. - Cambiaremos de nuevo el código para que utilice la columna original y deje de hacerlo en la nueva. Después de testarlo se subirá a producción.
- Y por último ya podremos borrar la columna de la contraseña nueva porque ya no será necesaria.
El punto 2 en el que generamos las nuevas contraseñas cifradas es un proceso largo dependiendo de cuantos usuarios tengamos en la base de datos y de lo rápida que sea nuestra máquina en hacer este cambio ya que generalmente tendrá que leer la contraseña en plano almacenada en la base de datos, cifrarla y luego asignar el resultado a la nueva columna para la nueva contraseña, y así uno por uno por cada usuario que tengamos. Esto puede dar pie a que cuando se termine el proceso ya hayan cambiado algunas de las contraseñas originales, cuidado con esto.
En cuanto al punto 5 y 6 debe estar optimizado para que no haya retrasos y se produzcan efectos como que se haya generado una contraseña en la columna nueva (un nuevo usuario por ejemplo) después de haber hecho el update y por tanto nunca aparezca en la columna original. Para esto se puede utilizar trucos como meter los dos pasos en una única transacción o cambiar el código momentáneamente para que cree las contraseñas sobre las dos columnas, etc. Todo va a depender del tráfico que tengáis, los servidores que utilicéis (un mysql con master slave también puede ayudar por ejemplo) para que esto realmente sea un problema. De todas formas lo suyo es luego hacer tracing de la base de datos para ver que todo se ha quedado bien.
Pasar de contraseñas en Hash inseguras a cifradas en un sistema más seguro
Hacer está migración es bastante más complicada que la anterior ya que al no tener las contraseñas originales en plano es imposible generar el nuevo Hash con Salt. Pero no está todo perdido, si lo piensas si que hay un punto en el que podemos obtener la contraseña en plano original y es en el momento de la autenticación ya que el usuario introducirá su contraseña, luego con un hash tipo sha1 lo que se haría es generar el sha1 y comparar, si da ok es que lo que nos pasaron era la contraseña en plano original.
Aprovechando esto lo que suelo hacer yo es utilizar una técnica que se suele conocer como hacer un cambio o actualización «lazy» o dicho en español «perezosa». En qué consiste? básicamente en que cada vez que un usuario se autentica comprobamos su contraseña con el sistema antiguo, si este da ok entonces utilizamos la contraseña en plano obtenida para generar la contraseña cifrada nueva y guardarla. Si todo va bien poco a poco los usuarios irán migrando de un sistema a otro de forma transparente para ellos. Sin darse cuenta terminan con una contraseña almacenada de forma mucho más segura. Veamos como lo haríamos.
- Igual que en caso anterior añadimos una nueva columna a nuestra tabla en base de datos que será donde se almacena la nueva contraseña cifrada.
- Cambiamos nuestro código de forma que intente autenticar con el sistema nuevo, si da error lo que hacemos es intentarlo con el antiguo y en caso de que de ok con el segundo entonces generamos la nueva contraseña y la guardamos, al mismo tiempo ponemos a null la vieja.
- Después de testear el nuevo código lo subimos a producción.
- Haremos tracing de la base de datos para ver que efectivamente las contraseñas se están migrando correctamente a medida que los usuarios van entrando.
Esta técnica es una buena opción porque nos evita forzar a todos los usuarios a cambiar la contraseña, pero tiene una pega, no nos asegura que todos los usuarios terminen migrando la contraseña ya que siempre tendremos usuarios residuales que nunca volverán a autenticarse. Lo que se puede hacer es que después de que pase un tiempo que dependerá de los usuarios que tengáis, realizar una limpieza final de la base de datos dejando sin contraseña a los usuarios residuales y de este modo forzándoles a generar una nueva en caso de que vuelvan a entrar.
Si tenéis alguna duda sobre estos procesos la podéis dejar en los comentarios. Chauuuu.