El problema
Actualmente estoy diseñando el backend para un SPA (aplicación de una sola página), que planeo construir de una manera bastante REST. El backend idealmente será solo una capa delgada entre el cliente y la base de datos. Casi todos los datos de la base de datos estarán relacionados con un usuario específico, por lo que necesito algún tipo de sistema de autenticación.
Dado que vivimos en un universo donde los humanos son bastante predecibles y las máquinas pueden ser realmente realmente , obviamente, tenemos que hacer que el hashing de contraseñas y la verificación de contraseñas sean lentas (normalmente se utilizan esquemas de estiramiento de claves como bcrypt o todo eso) ). Todo esto está bien y es excelente, pero nos complica la vida a las pobres almas que desean diseñar aplicaciones rápidas y rápidas utilizando un backend RESTful, ya que idealmente ese backend no tendría que almacenar ningún dato de sesión, sino simplemente autenticar cada solicitud individualmente ( utilizando, por ejemplo, la autenticación de acceso básico).
Sin embargo, esto significaría cambiar la contraseña de los usuarios para cada solicitud, lo que agregaría una penosa pena para cada solicitud (y posiblemente facilitaría los ataques DDoS). Por supuesto, este problema se puede resolver mediante el almacenamiento en caché de las credenciales de los usuarios en la memoria en el backend, pero esa solución no se siente muy limpia y presenta otros problemas, como manejar la retirada de la caché y obligar al cliente a almacenar las credenciales en texto sin formato.
En resumen, mi problema es que necesito algún tipo de sistema para manejar la autenticación de los usuarios de forma ágil, pero idealmente aún puedo evitar cualquier estado del lado del servidor.
Mi solución propuesta
En primer lugar, el tráfico entre el cliente y el servidor se cifrará utilizando el estándar TSL bog, por lo que no deberíamos ser vulnerables a los intrusos ni a los ataques de intermediarios.
La solución en la que he pensado es emitir un token para el cliente, después de la autenticación inicial exitosa usando las credenciales enviadas en texto sin formato a través de TSL, que contiene la información necesaria para que el servidor autentique a un usuario. Este token se calcularía de la siguiente manera:
key = a random key generated when the server starts, never to be shared
nonce = just some random bytes grabbed out of the air
token = nonce + timestamp + user_id + HMAC(key, nonce + timestamp + user_id)
Esto permitiría al servidor verificar si el token es válido simplemente validando el HMAC para cada solicitud, lo cual es muy barato de hacer (casi tan barato como hacer una búsqueda en una tabla hash, pero evitando por completo la necesidad de una tabla de hash en primer lugar). Si el token es válido y no ha caducado (no se debe permitir que la marca de tiempo sea demasiado antigua) Dejo que la solicitud continúe, y dejo que la base de datos maneje el problema de autorización.
Esto se siente como una manera de autenticar a los usuarios de manera eficiente y segura, además de requerir sorprendentemente pocos LoCs para implementar, así como evitar totalmente cualquier tipo de estado en el servidor (la aplicación del servidor usaría una cantidad constante de memoria durante toda su vida útil) ).
Mi verdadera pregunta
Ahora bien, este esquema puede parecer totalmente inestable a primera vista, pero después de verlo por un momento, veo dos posibles problemas con él que generan preguntas:
-
No hay forma de retirar un token, excepto esperar a que caduque. ¿Es esto realmente un problema o soy yo paranoico? Al ver que debe almacenarse en el lado del cliente, la seguridad se ve comprometida de inmediato si alguna parte maliciosa obtiene acceso a un cliente antes de que caduque el token. Claro, el peligro está contenido en el intervalo de tiempo durante el cual el token es válido, pero para este SPA ese intervalo de tiempo puede contarse en días.
-
¿Cómo escalamos? La clave del lado del servidor secreto tendría que ser compartida entre todos los servidores del clúster, pero ¿cómo lo haríamos de manera segura, y cuándo deberíamos retirar una clave del lado del servidor?
También puede haber otros problemas, pero estos son los dos que se destacaron más que yo. ¿Qué opinas sobre este esquema y estos problemas?
Tenga en cuenta que esta pregunta no es una pregunta general sobre la generación segura de tokens o la administración de sesiones (como se ha respondido muchas veces aquí), sino sobre los problemas periféricos relacionados con este esquema específico, problemas que no he visto discutidos.