Fichas de seguridad para exposición externa: ¿UUID o HMAC / JWT / hash?

1

Estoy creando el backend para una aplicación web. Cuando un nuevo usuario ingresa al sitio y hace clic en el botón Registrarse , completará un formulario súper simple que les pedirá su nombre de usuario y contraseña y los enviará. Esto solicita al servidor que envíe un correo electrónico de verificación a esa dirección de correo electrónico. Luego revisarán su correo electrónico, harán clic en un enlace (que verifica su correo electrónico) y luego serán enviados a la página de inicio de sesión para que puedan iniciar sesión si así lo desean.

Para verificar su correo electrónico, cuando el servidor genere el correo electrónico, deberá crear (y almacenar) un token de verificación (probablemente un UUID) y adjuntarlo a este enlace en el correo electrónico, para que el enlace se vea algo como:

  

" enlace "

Donde vt=12345 es el "token de verificación" (también es probable que sea un UUID). Así que el usuario hace clic en este enlace y mi punto final GET v1/users/verify mira el token, de alguna manera confirma su validez y realiza algunas actualizaciones de la base de datos para "activar" al usuario. Ahora pueden iniciar sesión.

Escenarios similares para cuando un usuario desea darse de baja de recibir correos electrónicos, o cuando no puede recordar su contraseña y necesita recuperarla para poder iniciar sesión.

Cancelar suscripción

El usuario quiere dejar de recibir correos electrónicos, pero aún quiere usar la aplicación. Hacen clic en el enlace " Cancelar suscripción " en un boletín semanal que les enviamos. Este enlace debe contener algún tipo de "token de cancelación de suscripción" similar que, como el token de verificación anterior, se genera + almacena en el servidor y se usa para autenticar la solicitud del usuario para cancelar la suscripción del correo electrónico.

Recuperar contraseña

Aquí el usuario ha olvidado su contraseña y necesita recuperarla. Así que en la pantalla de inicio de sesión, hacen clic en el enlace " Olvidé mi contraseña " y se les presenta un formulario donde deben completar su dirección de correo electrónico. El servidor envía un correo electrónico a esa dirección. Verifican este correo electrónico y contiene un enlace a un formulario donde pueden ingresar su nueva contraseña. Este enlace debe contener un "token de restablecimiento de contraseña" que, al igual que el token de verificación anterior, se genera + almacena en el servidor y se usa para autenticar la solicitud del usuario para cambiar su contraseña.

Por lo tanto, aquí tenemos tres problemas muy similares que resolver, todos requieren el uso de lo que llamo " tokens de seguridad de una sola vez (OTO) ". Estos tokens OTO:

  • Se debe generar en el lado del servidor y persistir (tal vez en una tabla security_tokens )
  • Debe ser algo que se pueda adjuntar a los enlaces que expondremos desde el interior de los correos electrónicos
  • Solo debe ser válido una vez: una vez que hacen clic, el token se "usa" y no se puede reutilizar.

Mi pregunta

La solución que encontré fue simple ... casi demasiado simple.

Para los tokens, estoy generando UUID aleatorios (36 caracteres) y almacenándolos en una tabla security_tokens que tiene los siguientes campos:

[security_tokens]
---
id (PK)
user_id (FK to [users] table)
token (the token itself)
status (UNCLAIMED or CLAIMED)
generated_on (DATETIME when created)

Cuando el servidor los crea, son "NO RECLAMADOS". Cuando el usuario hace clic en un enlace dentro de la tabla, está "RECLAMADO". Un trabajo de segundo plano se ejecutará periódicamente para limpiar los tokens RECLAMADOS o para eliminar los tokens NO RECLAMADOS que hayan "caducado" (según sus campos generated_on ). La aplicación también ignorará cualquier token que haya sido RECLAMADO anteriormente (y que aún no se haya limpiado).

Pienso que esta solución funcionaría, pero no soy un tipo de súper seguridad y me preocupa que este enfoque:

  1. Posiblemente deja mi aplicación abierta a algún tipo de ataque / explotación; y
  2. Posiblemente reinvente la rueda cuando alguna otra solución podría funcionar igual de bien

Al igual que para el segundo anterior, me pregunto si debería usar un mecanismo relacionado con hash / HMAC / JWT en lugar de un UUID simple muerto. Tal vez hay gente inteligente de seguridad / criptografía que encontró una manera de hacer que estos tokens contengan el estado de RECLAMACIÓN y la fecha de caducidad de una manera segura / inmutable, etc.

    
pregunta smeeb 09.01.2018 - 16:13
fuente

1 respuesta

1

Veo algunas preocupaciones con lo que has descrito:

  1. Dependiendo de cómo genere los UUID, es posible que no sean tan impredecibles, son no tokens criptográficamente fuertes .
  2. No describe un "propósito" en su tabla security_tokens , que podría (dependiendo de su código) permitir que alguien use un token para un propósito diferente al que estaba destinado.
  3. Su sistema requiere una gran cantidad de tráfico en la base de datos, esto podría o no ser un problema, dependiendo de la cantidad de tráfico que anticipa recibir.

Una solución alternativa sería utilizar (por ejemplo) JWT e incluir en los datos firmados:

  1. El ID de usuario para el que es válido.
  2. El propósito por el cual es válido.

JWT en sí mismo proporciona la funcionalidad para administrar los vencimientos. Tenga en cuenta que esto genera tokens que se pueden "gastar dos veces", pero si son para validar correos electrónicos o cancelar la suscripción, eso no es un problema. (¿La validación del mismo correo electrónico es dos veces una preocupación?)

Para estos tokens, todo lo que necesita es un secreto almacenado en el servidor de aplicaciones, y no incurre en escrituras de base de datos adicionales para generar o gastar los tokens. (Lo que, una vez más, es un problema de escala, puede o no aplicarse a su aplicación).

    
respondido por el David 09.01.2018 - 17:50
fuente

Lea otras preguntas en las etiquetas