Hoy tuve que hacer mantenimiento en un viejo sitio en PHP y fue un buen recordatorio de cómo gestionábamos sesiones… y cómo existen alternativas más modernas, escalables y flexibles. Una de ellas es JWT.
JWT significa JSON Web Token. Es un estándar abierto que nos permite intercambiar información de forma segura entre dos partes como un objeto JSON. Usando esto, en lugar de guardar la sesión del usuario en el servidor, se genera un token firmado criptográficamente que contiene los datos necesarios para identificar a ese usuario. En cada solicitud, en lugar de la cookie de sesión, se envía este token para decodificarlo e identificar al usuario en el servidor.
Este pequeño cambio de enfoque, dejar de guardar el estado de la sesión del lado del servidor y delegar esa responsabilidad al cliente, es lo que llamamos invertir el control de las sesiones.
Una de las dudas más comunes al leer que “los datos viajan con el cliente” es qué pasa si lo modifica. Aquí es donde entra en juego una parte clave del JWT: La firma.
Un JWT está codificado en base64 para evitar problemas con caracteres especiales, pero base64 no es un algoritmo de seguridad, también está cifrado criptográficamente usando una clave secreta, o un par de claves pública/privada. Esa firma asegura que, aunque el token viaje por la red y esté guardado en el navegador del usuario, no pueda ser modificado sin que el servidor se dé cuenta. Si alguien intenta cambiar algo del contenido del token (por ejemplo, hacerse pasar por otro usuario), la firma deja de ser válida, y el servidor lo rechaza.
Es que lo mismo sucede con gran parte de la criptografía, particularmente en los sistemas de clave pública, y este es un detalle para entenderla un poco mejor: Cualquiera puede modificar o generar nuevo contenido cifrado. Si no fuera así, los navegadores no podrían enviar datos por https. La clave está en que el sistema puede decirnos con certeza (¿por ahora?) si el mensaje fue alterado o no.
En resumen, los datos están del lado del cliente, pero el control sigue siendo del servidor gracias a la firma.
Otra ventaja muy fuerte de los JWT es que no dependen de las cookies. A diferencia de las sesiones clásicas que se basan en una cookie de sesión para mantener el estado entre pedidos, los JWT pueden ser guardados en cualquier lugar del lado del cliente. Si bien sería válido guardar un JWT en una cookie, es decisión del programador, y suelen guardarse en cualquier tipo de almacenamiento local del navegador o de una app móvil.
Esto da mucha más flexibilidad, sobre todo en aplicaciones modernas donde queremos tener control explícito sobre cómo se maneja la autenticación, usamos APIs, estamos en una app nativa (no navegador), etc.
Otra clara ventaja es que se elimina la necesidad de mantener sesiones en la memoria del servidor. No hay que guardar un array de sesiones activas, cada solicitud trae toda la información necesaria para validar al usuario, lo que se traduce en un sistema escalable en (al menos) dos sentidos: Desaparece la restricción de memoria por usuario, y en un sistema distribuido (múltiples servidores corriendo la app en paralelo) cualquier servidor puede atender la solicitud sin problemas, incluso si el servidor donde el usuario inició sesión originalmente deja de estar disponible, sin necesidad de infraestructura adicional para eso.
Entre las desventajas de usar JWT podemos mencionar que, dado que el servidor no tiene forma de «recordar» quiénes son los usuarios conectados, no pueden ser fácilmente invalidados, como por ejemplo forzando un cierre de sesión. Esto puede traer complejidad adicional si lo necesitamos.
Además, los tokens no son livianos y pueden llegar a ser bastante más pesados que una simple cookie de sesión. Si se mandan con cada request (como suele hacerse en APIs), eso puede tener un impacto acumulado, especialmente en conexiones móviles.
Y como siempre, la seguridad depende de cómo lo implementemos: sin https, sin validación correcta de la firma, o con claves débiles, cualquier esquema de autenticación se vuelve vulnerable.