2024/04/13
Tabla de contenidos
En septiembre del año pasado decidí empezar un nuevo proyecto, una plataforma de adopción para mascotas. Era una idea que tenía desde hacía tiempo pero simplemente no había tenido tiempo para empezar a trabajar en ella, pero ahora que tenía suficiente tiempo y que también quería aprender más sobre el desarrollo de aplicaciones web, en el sentido de cómo se implementan, empecé a trabajar en ello.
Elegí Flask para empezar a desarrollar la aplicación web porque me sentía cómodo usando Python y porque Flask es un framework popular y sencillo. Debido a su simplicidad no tiene ninguna funcionalidad de gestión de usuarios ni un ORM de base de datos, pero sí permite escribir extensiones para ampliar sus funciones. Como mi aplicación web va a tener usuarios, necesitaré una base de datos para almacenarlos, así que en lugar de escribir consultas SQL puras y tener que lidiar con ataques de inyección SQL, consultas complejas y no optimizadas, elegí usar un ORM (mapeador de objetos relacionales), SQLAlchemy, que es el ORM más popular para Python. Los beneficios de un ORM como SQLAlchemy, aparte de lo mencionado anteriormente, es que soporta múltiples bases de datos, por lo que con solo cambiar la URL en la configuración puedes cambiar la base de datos que utilizas sin tener que lidiar con posibles problemas de sintaxis en tu consulta.
Una de las cosas que considero importante es evitar la sobrecarga de software (bloat) e intentar cuidar esos detalles al escribir código, especialmente cuando algunos lenguajes y herramientas proporcionan gestión de dependencias que facilita la adición de código a tu software, lo cual en sí mismo no es malo, las dependencias nos permiten evitar reinventar la rueda y centrarnos en escribir nuestra solución. El problema es cuando esto lo hace innecesariamente complejo. Por esa razón decidí usar la función de plantillas de Flask usando Jinja2 para crear el HTML en lugar de usar Flask como backend y el frontend ejecutándose con algo como react, vue, angular, etc.
El diseño en sí es muy sencillo y no utiliza nada de Javascript. He intentado usar algunos sitios web que dependen fuertemente de Javascript en mi teléfono y son prácticamente inutilizables; se vuelven lentos y no responden, se podría argumentar que es simplemente culpa de malos desarrolladores/diseñadores pero ¿realmente hace falta Javascript? Al no utilizar Javascript, los dispositivos de gama baja no deberían tener problemas para cargar el sitio web, especialmente los navegadores (clientes) que podrían no soportar Javascript. Claro que no tendrá un menú de hamburguesa sofisticado que se desvanezca cuando hagas clic y se desvanezca al hacer clic en otra parte, pero de nuevo, en este caso, ¿realmente es necesario?
Mientras desarrollaba la aplicación web aprendí bastantes cosas, algunas ya las sabía pero solo de forma teórica por leer o ver explicaciones.
Flask tiene una utilidad de sesiones que permite almacenar información de un usuario mediante cookies. Así que, cuando un usuario se autentica, almacenas su ID de usuario usando cookies y esas cookies están firmadas criptográficamente. Lo que significa que pueden ver el contenido de la cookie pero si la modifican (sin conocer tu SECRET_KEY) será inválida. El problema con el enfoque de sesión en el lado del cliente es que no tienes mucho control sobre esa cookie; por ejemplo, si quieres borrar todas las sesiones después de que un usuario cambie su contraseña, no podrás saber si esa sesión fue antes o después de la actualización de la contraseña porque solo contiene el ID de usuario, como solución alternativa podrías almacenar el ID de usuario Y una marca de tiempo de cuando se cambió la contraseña para que cada vez que recibas una petición puedas comprobar que la marca de tiempo es la misma que la almacenada en tu base de datos.
Implementé una sesión en el lado del servidor, por lo que es más fácil mantener un seguimiento de las sesiones activas y controlarlas.
Con la autenticación, siempre leía y veía vídeos sobre el hashing de contraseñas y el uso de sales (salting), sabía cómo funcionaba en teoría pero no terminaba de entender cómo el servidor podía autenticar una sesión si la sal era supuestamente aleatoria y podía ponerse en cualquier parte de la contraseña, cómo podía ser mejor que el hashing si de todas formas tenían que guardar la sal en la base de datos.
Flask proporciona una utilidad a través de werkzeug que te permite generar un hash de contraseña+sal y también una que comprueba un hash de contraseña.
Por ejemplo, si generamos una contraseña con generate_password_hash("pass") se genera una salida como esta:
scrypt:32768:8:1$klNz0yomOYCC3MU6$d14ca08dceb53989e959481aa94e625240c82aecdc13dec598be9e41837c819b220844cc256aacd612799c396722cfcdb6fa79991f7d68f43d8aa54b2bf0cb1f
Parece larga y complicada porque lo es, pero puedes ver que la salida utiliza $ para separar los campos.
scrypt:32768:8:1 contiene los parámetros para el método de hash separados por :.klNz0yomOYCC3MU6 es la sal que se añadió a la contraseña.d14ca08dceb53989e959481aa94e625240c82aecdc13dec598be9e41837c819b220844cc256aacd612799c396722cfcdb6fa79991f7d68f43d8aa54b2bf0cb1f es el hash+sal de la contraseña.
|
|
Cada vez que llamas a generate_password_hash() se generará una sal aleatoria para la contraseña dada, generando un hash completamente diferente para la misma contraseña.
Enviar correos electrónicos usando python es sencillo, tienen varios ejemplos en su documentación. La parte que me costó entender fue la configuración del servidor de correo para poder enviar correos correctamente configurados.
Configurar postfix para enviar correos fue sencillo, hay muchas guías que puedes encontrar en internet, esa fue la parte fácil. El problema es que solo configurar postfix no es suficiente si no quieres que tus correos sean rechazados o enviados directamente a la carpeta de spam de la gente; muchos correos de phishing abusan del hecho de que pueden enviar correos en nombre de otro dominio, razón por la cual algunos correos pueden parecer venir de @legitbusiness.com pero en realidad son phishing enviados desde otro dominio; por esa razón, hoy en día los servidores de correo requieren la configuración de DKIM y SPF como métodos de autenticación al enviar correos, de lo contrario tu correo será rechazado o enviado a la carpeta de spam.
Configurar DKIM y SPF me llevó un tiempo configurarlos correctamente y muchos correos enviados, porque una vez que los has configurado, necesitas comprobar si se están recibiendo correctamente.
Aprendí bastantes cosas de este proyecto; aunque ya sabía algunas cosas solo de forma teórica, creo que aplicar ese conocimiento me hizo aprender más sobre ellas y cómo funcionan. También fue divertido desarrollar la plataforma y ver cómo se iba formando poco a poco.
Para futuros desarrollos, probablemente me gustaría abordar algo como esto con la escalabilidad en mente, por ejemplo, diseñándolo con microservicios para que pueda escalar fácilmente de forma horizontal, como una forma de reto.