Autenticación basada en tokens utilizando tokens de acceso y actualización

8

Estoy implementando un sistema de autenticación basado en token para una API REST usando un token de acceso de corta duración y un token de actualización de larga duración. Esta es una descripción general de los puntos finales API relevantes (HTTPS se aplica para todos los puntos finales):

Puntos finales:

POST /register/
POST /login/
POST /logout/
POST /password/change/

Implementación:

POST /register/:

  • Solicitud: el cliente envía un nombre de usuario, correo electrónico y contraseña en JSON.
  • Acciones del servidor:
    1. Valida la entrada, crea un usuario en la base de datos (almacena el ID de usuario, nombre de usuario, correo electrónico y hash de contraseña).
    2. Crea un token de acceso de corta duración en formato JWT (contiene el ID de usuario, la fecha de emisión y la fecha de vencimiento).
    3. Crea un token de actualización de larga duración como una cadena UUID y lo almacena en la base de datos (almacena la identificación del usuario y el token de actualización).
  • Respuesta: El servidor devuelve el token de acceso y el token de actualización en JSON.

POST /login/:

  • Solicitud: el cliente envía el nombre de usuario y la contraseña en JSON.
  • Acciones del servidor:
    1. Valida la entrada, verifica si las credenciales son válidas verificando la base de datos.
    2. Si las credenciales son válidas, crea un token de acceso de corta duración y un token de actualización de larga duración como se mencionó anteriormente.
  • Respuesta: Igual que /register/, devuelve el token de acceso y el token de actualización en JSON.

POST /logout/:

  • Solicitud: el cliente envía un token de actualización en el Authorizationencabezado como Bearertoken.
  • Acciones del servidor:
    1. Valida el token de actualización marcando la base de datos de token de actualización.
    2. Elimina el token de actualización de la base de datos.
      Nota: Esto deja el token de acceso válido, pero dado que será de corta duración (1 hora más o menos, creo que debería estar bien).
  • Respuesta: Devuelve si la solicitud de cierre de sesión se procesó correctamente en JSON.

POST /password/change/:

  • Solicitud: el cliente envía el token de acceso en el Authorizationencabezado como Bearertoken, y también envía la contraseña anterior y la nueva contraseña en JSON a través de HTTPS.
  • Acciones del servidor:
    1. Decodifica el token de acceso para recuperar al usuario y verifica la contraseña anterior del usuario con la base de datos.
    2. Establece el hash de contraseña del usuario en la base de datos en el hash de la nueva contraseña.
    3. Elimina todos los tokens de actualización asociados con el usuario en la base de datos de tokens de actualización para cerrar la sesión de las sesiones existentes (deja válidos los tokens de acceso de corta duración).
  • Respuesta: Devuelve si la solicitud de cambio de contraseña se procesó correctamente en JSON.

Preguntas:

  1. ¿Es seguro este enfoque? Específicamente:
    • ¿Es seguro enviar el nombre de usuario y la contraseña a través de JSON si se realiza a través de HTTPS? ¿Cómo evitaría que los dominios no autorizados realicen llamadas a este punto final? Además, ¿cómo evitaría los inicios de sesión programáticos?
    • ¿Deberían los tokens de actualización ser procesados ​​antes de almacenarlos en la base de datos, o simplemente estoy siendo paranoico?
  2. Si el cliente fuera un navegador web, ¿cómo almacenaría de forma segura el token de actualización en el cliente?
    • Una idea que tengo para almacenar el token de actualización es: cuando el usuario inicia sesión, además de enviar el token de actualización al cliente, el servidor almacena el token en una HttpOnlycookie con un secureindicador. La autorización aún se realizará a través del Authorizationencabezado, pero cuando el cliente se carga inicialmente, puede enviar una GETsolicitud a un punto final que verifique si la cookie contiene un token de actualización válido y, de ser así, devolverlo al usuario en JSON. En otras palabras, el único momento en que la cookie se usará realmente es devolver el token de actualización dentro de la cookie al cliente. ¿Es seguro este enfoque? Creo que evitará CSRF ya que no hay efectos secundarios al solicitar el token de actualización de la cookie, pero ¿hay alguna otra forma en que un atacante pueda interceptar el token de actualización (suponiendo HTTPS)?
Kootling
fuente
2
¿Por qué no utilizar Open ID Connect en lugar de intentar implementar su propio mecanismo de seguridad de inicio de sesión?
Erik Eidt
No quiero tener que manejar un servidor adicional para que actúe como proveedor de conexión OpenID. Además, preferiría tener un control total sobre el flujo de autenticación para poder identificar fácilmente errores y vulnerabilidades de seguridad.
Kootling 01 de
2
No creo que requiera un servidor adicional si solo lo usa. Además, hacer su propia implementación con su propio protocolo tiene sus propios riesgos de seguridad, tal vez más que usar el enfoque estándar emergente, pero ¿quién sabe con certeza?
Erik Eidt

Respuestas:

2

¿Es seguro este enfoque? Específicamente:

  • ¿Es seguro enviar el nombre de usuario y la contraseña a través de JSON si se realiza a través de HTTPS?

Si. Los encabezados, los parámetros de solicitud y el cuerpo de la solicitud se cifran durante la comunicación.

Una vez en el lado del servidor, no registre el cuerpo de la solicitud :-)

  • ¿Cómo evitaría que los dominios no autorizados realicen llamadas a este punto final?

No se puede. Básicamente, una vez que la API está en la WWW, se expone automáticamente a todo tipo de malicia. Lo mejor que puede hacer es estar preparado y estar al tanto de las amenazas. Al menos sobre los que te conciernen. Echa un vistazo aquí .

Un posible enfoque para el problema podría ser implementar (o contratar) un Administrador de API .

Los administradores de API locales pueden reducir la superficie de ataque porque todos los puntos finales detrás de la AM no son necesariamente públicos.

Podría lograr el mismo resultado con algunos productos en la nube, pero son absurdamente caros para la corriente principal.

De todos modos, los puntos finales de API Management permanecerán expuestos a ataques.

  • Además, ¿cómo evitaría los inicios de sesión programáticos?

Si por inicios de sesión programáticos te refieres a ataques por fuerza bruta, un umbral (número máximo de solicitudes permitidas por segundo) y una lista negra deberían ser suficientes para disuadir la insistencia del atacante. Para más información, mira aquí .

Muchos de los administradores de API proporcionan configuraciones de límite de velocidad de API y listas blancas listas para usar .

Si está familiarizado con la Consola API de Google, puede adivinar lo que puede hacer un Administrador de API.

  • ¿Deberían los tokens de actualización ser procesados ​​antes de almacenarlos en la base de datos, o simplemente estoy siendo paranoico?

Si el token de actualización es un UUID simple o cualquier otra cosa, no me gusta exponer este tipo de detalles de implementación. Por lo tanto, sugeriría hacer hash. Para mí, cuanto más opacos sean los detalles de implementación de la capa de seguridad, mejor.

En cuanto a la seguridad de JWT, eche un vistazo aquí .

  • Si el cliente fuera un navegador web, ¿cómo almacenaría de forma segura el token de actualización en el cliente?

Quizás le interese el JSON Web Token (JWT): almacenamiento en el lado del cliente .

Laiv
fuente
Con respecto a los inicios de sesión programáticos, quiero evitar que alguien haga su propio front-end a la API (aunque, como dijiste, esto es prácticamente imposible). Además, los tokens de actualización (que son UUID, no JWT) se almacenan actualmente como texto sin formato en la base de datos, por lo que definitivamente debería usar hash, ¿verdad? Con respecto al almacenamiento de tokens del lado del cliente, quiero almacenar los tokens de actualización y conservarlos en las sesiones, por sessionStoragelo que no funcionará. Además, localStoragey document.cookieparecen inseguros, ya que JavaScript puede acceder a ellos. ¿Mi enfoque tiene sentido o es potencialmente inseguro?
Kootling
1. Sí, almacenaría un token hash en lugar del texto sin formato. Solo por hacer que el contenido de la tabla sea opaco a otros procesos. 2. Si tengo que elegir entre localStorage y cookies, elijo las cookies. Sin embargo, primero investigaría un poco sobre sus vulnerabilidades. Además, considere agregar a la seguridad un mecanismo para invalidar los tokens manualmente.
Laiv
Por "cookies", ¿quiere decir document.cookieo una cookie HttpOnly con una bandera segura, similar al enfoque que mencioné anteriormente?
Kootling
HttpOnly y la bandera segura no te impiden XST y XSRF. Pero hace que la cookie sea segura para los ataques XSS, lo que considero básico. Entonces sí, HttpOnly y bandera segura. Solo recuerde que no tendrá acceso a la cookie desde Javascript
Laiv
De todos modos, te animo a que compruebes los diferentes proyectos de OWASP relacionados con aplicaciones web, API, REST y JWT. Encontrará mucha más información que la que puedo proporcionarle aquí.
Laiv