Mitigar la amenaza de ataque de tiempo contra la página de recuperación de contraseña

15

Recientemente, se realizó una revisión de seguridad externa en un sitio web público que administramos. Señalaron que en la "página de recuperación de contraseña", existen diferentes tiempos de respuesta cuando se proporcionan nombres de usuario existentes y no existentes. Afirman que esto podría facilitar que un atacante pruebe los nombres de usuario existentes. El cambio sugerido para eliminar este vector de ataque, es ejecutar siempre la misma lógica para cada intento de recuperación de contraseña.

En nuestro caso, la lógica de recuperación de contraseña consiste en (aproximadamente) los siguientes pasos:

  1. Cargar el usuario desde la base de datos.
  2. Envíe un correo electrónico al usuario con un enlace para restablecer la contraseña.

Si el usuario existe, ambos pasos se llevan a cabo. Si el usuario no existe, solo se ejecuta el paso 1.

En términos de búsquedas en bases de datos, etc., esto se resuelve fácilmente. Pero cuando se trata de interactuar con sistemas externos, en este caso enviar un correo electrónico, es menos trivial.

Supuse que introducir un retraso aleatorio no servirá de mucho, ya que es probable que todavía haya una diferencia detectable en las distribuciones de tiempo de respuesta.

Otra idea es configurar una cuenta de correo electrónico en algún lugar y enviar un correo electrónico allí por cada nombre de usuario que no exista.

¿Cuál sería la mejor práctica en esta situación?

    
pregunta MEMark 09.10.2015 - 15:55
fuente

7 respuestas

7

He visto esto hecho de varias maneras, una que ya mencionaste usando adiciones de tiempo aleatorias (no soy fan de esto), dos al hacer que la tarea sea asíncrona como ha dicho phyrfox (esto funciona bien si puedes hacerlo), pero más recientemente, tres ; por tener una duración mínima forzada para la respuesta de la página. No estoy seguro exactamente de lo que pienso sobre este método vs dos, pero lo detallaré a continuación de todos modos:

La lógica básica en tu situación podría ser:

  1. marca de tiempo del sistema de tienda
  2. cargar usuario desde la base de datos
  3. Envíe un correo electrónico al usuario con un enlace para restablecer la contraseña
  4. verifique la diferencia de tiempo entre ahora y la marca de tiempo del paso 1. Si la diferencia x es menor que la duración mínima
  5. retrasar la finalización de la respuesta en x menos el tiempo en pasta hasta ahora.

Esto tiene un par de puntos buenos y un par de puntos malos ..

bien:

  • Algunos otros mecanismos de nivel inferior se centran en hacer que cada paso atómico tome la misma cantidad de tiempo, en su escenario, necesita asegurarse de que el tiempo total de todos los pasos sea el mismo, esta es una trampa para hacerlo. .
  • es una implementación de código rápida y fácil.

malo:

  • si alguien puede causar una demora en un componente, puede extender el tiempo tomado más allá de una cantidad mínima codificada. Por ejemplo, si en promedio el viaje de ida y vuelta duró 1 segundo, podría imponer una duración mínima de 1,5 segundos. Si el tiempo de respuesta del servidor de la base de datos aumenta debido a una actividad benigna o malintencionada, puede impulsar el tiempo de respuesta promedio hasta 2 segundos, más allá de su duración mínima. Puede intentar evitar esto utilizando una duración mínima dinámica, basada en la duración promedio de las solicitudes recientes, por ejemplo (pero con un valor mínimo establecido).
  • está haciendo más fácil que su sistema sufra un DOS por múltiples solicitudes a páginas con este tipo de mecanismo. El lugar que vi usando este truco recientemente vio este riesgo como el menor de dos males.

Editar

Ya que no puedo hacer comentarios sobre otras respuestas, solo quería señalar que en los sistemas que no tienen registros abiertos, debes intentar mantener el nombre de usuario en secreto, e independientemente de eso, a menos que tus nombres de usuario estén expuestos por diseño durante el período normal. Uso de tu sistema - ¡defensa en profundidad personas! ¿Por qué hacerlo más fácil de lo que debe ser?

Justo en la parte superior de mi cabeza: si puedes enumerar a miles de usuarios rápidamente, aumenta el número de cuentas que podrían tener contraseñas simples para fuerza bruta, o quizás estarías exponiendo a tus usuarios a otros vectores de ataque como Spear phishing o ataques de ingeniería social. Cuanta menos información tengan los malos sobre sus usuarios, mejor.

    
respondido por el GreatSeaSpider 09.10.2015 - 16:52
fuente
11

Hagamos la verdadera pregunta aquí:

¿La lista de nombres de usuario asociada con su aplicación es secreta?

Tipo de, pero en su mayoría NO . Por lo general, cada nombre de usuario es único en una aplicación, por lo que es muy difícil, si no imposible, mantenerlo en secreto. Por ejemplo, una forma que a menudo funcionará (si no siempre) para encontrar un nombre de usuario asociado con su aplicación es simplemente crear una nueva cuenta. Cuando ingrese un nombre de usuario, el proceso de creación le dirá si ya está tomado o no. Por lo tanto, puede construir lentamente una lista de nombre de usuario válido.

En general, no tiene mucho sentido tratar de mantener el nombre de usuario en secreto y probablemente sea imposible, así que simplemente renuncie a esa parte.

Lectura interesante sobre cómo Su nombre de usuario no es un secreto .

¿Qué debería tratar de proteger?

Debes proteger los secretos asociados con el nombre de usuario; Contraseña y cualquier otra forma de autenticación. Es aquí donde tiene sentido preocuparse por el momento del ataque. Por ejemplo, su código de verificación de contraseña debe tardar al mismo tiempo si los primeros 2 caracteres coinciden que si no coincide ningún carácter.

Nota adicional

Otro tema interesante es proteger tu autenticación multifactor. No es directamente un ataque de tiempo, pero es el mismo principio. Por ejemplo, si tiene una contraseña y un código telefónico necesarios para iniciar sesión en una aplicación. Si es la aplicación, solo pregúntele sobre el código del teléfono cuando la contraseña sea correcta, al atacante le resultará mucho más fácil encontrar la contraseña que si la aplicación le pidiera la contraseña Y el código del teléfono cada vez. Pero hay algún problema sobre la privacidad del número de teléfono si lo preguntas cada vez ...

    
respondido por el Gudradain 09.10.2015 - 16:34
fuente
10

Puede elegir realizar las acciones de forma asíncrona, dando la apariencia de tener un tiempo lineal. En términos de pseudocódigo, siempre puedes hacer algo como esto:

Sleep 1000 ; Sleep for a second
Output template ; Show a confirmation page
Close Socket ; Make the browser/client believe we're done
Lookup User From Database ; Try to load the user
If User Found
    Send Email ; Send an Email
End If

Sé de al menos un servicio que hace esto; el usuario siempre verá un mensaje de confirmación ("Genial. Ahora revise su correo electrónico") sin confirmar realmente si el usuario existe y / o se envió un correo electrónico. Esto elimina todos los tiempos variables bajo su control, incluidos los tiempos de búsqueda en la base de datos y el tiempo que demore el envío del correo electrónico.

No todos los idiomas admiten este tipo de modelo directamente, pero es posible que pueda usar fork () para generar un proceso (o cualquier método que proporcione su idioma) para realizar el código de forma asíncrona.

Esto deja problemas de tiempo solo para el programador de tareas del sistema operativo y no se basa en la lógica real realizada. La introducción de un retraso artificial también mitigará algunos tipos de programas que no son de multiproceso, ya que tendrán que esperar un segundo para cada intento (no es una buena defensa, pero muchas herramientas de "pirateo" no son de forma masiva y de múltiples hilos). / p>

Sin embargo, incluso sin la demora, aún no podrían confirmar si la cuenta de usuario era realmente válida o no, por lo que el ataque se mitigaría. Lo único que debe hacer es estabilizar la cantidad de tiempo que se tarda en recibir una respuesta; una búsqueda exitosa y una búsqueda fallida deberían tomar la misma cantidad de tiempo.

    
respondido por el phyrfox 09.10.2015 - 16:24
fuente
4

La aleatorización de cualquier tipo solo mitigará el ataque y requerirá más estadísticas antes de poder observar una diferencia en el tiempo. Esa no es una buena solución, a menos que sea lo mejor que se pueda hacer.

Mi sugerencia, en cambio, es tener un proceso en segundo plano que envíe correos electrónicos. El proceso de recuperación real ni siquiera verificará si el nombre de usuario es válido, solo enviaría un mensaje al proceso en segundo plano (a través del método de IPC que sea más conveniente) diciendo que envíe el correo electrónico para el nombre de usuario tal y tal y de inmediato devuelva el " página "hecha".

El proceso en segundo plano se manejaría para ver si el nombre de usuario es válido y enviar el correo electrónico si es así, pero sus resultados y tiempo no deberían ser observados por un atacante remoto.

    
respondido por el otus 10.10.2015 - 07:56
fuente
0

Para resolver esto, todo lo que tiene que hacer es permitir que el programa envíe la página de respuesta en ciertos intervalos de reloj de pared. De esta manera, siempre que el tiempo real requerido para finalizar el procesamiento sea más corto que el tiempo entre un intervalo, siempre responderá de una manera que sea inútil para este ataque.

Entonces, al elegir un intervalo, sé generoso.

Lo que quiero decir es: La página de recuperación de contraseña solo puede responder, por ejemplo, 4 veces por minuto.

[00:00]

[00:07] Comienza el procesamiento

[00:09] (El proceso se completa para un nombre de usuario no válido)

[00:14] (El procesamiento se completa para un nombre de usuario válido)

[00:15] (esperando ...)

[00:30] (Respuesta enviada)

[00:45]

[01:00]

...

Si el procesamiento finaliza entre cualquiera de esos intervalos, espera hasta el siguiente intervalo para responder.

    
respondido por el Hydranix 10.10.2015 - 09:16
fuente
0

Creo que hay una solución muy simple para este problema y estoy un poco sorprendido de que no se haya mencionado. Dejando a un lado todas las preguntas sobre si los nombres de usuario son secretos o no, todo lo que necesita hacer es cambiar la lógica.

En lugar de verificar que el usuario sea válido y luego mostrarle el mensaje, simplemente responda con una respuesta estándar para todos los intentos de restablecimiento de contraseña y haga esto ANTES de intentar verificar la cuenta. Simplemente envíe una respuesta siguiendo las líneas "Si tiene una cuenta válida en este sistema, se le enviará un enlace de restablecimiento de contraseña a su dirección de correo". Si hace esto primero, obtendrá (aproximadamente) el mismo tiempo de respuesta para solicitudes válidas e inválidas. Después de enviar la respuesta, verifique los datos y, si se ve legítimo, envíe el correo electrónico.

La única compilación será que necesitará configurar esto de manera que la etapa que maneja el envío del enlace de correo electrónico deba realizarse en un proceso separado para que pueda responder y finalizar la conexión web inicial. Esto se puede hacer fácilmente simplemente registrando la solicitud en una tabla de base de datos y teniendo otro proceso que busque nuevos registros en esta tabla en un horario regular y los procese en consecuencia.

    
respondido por el Tim X 16.10.2015 - 00:40
fuente
-1

Agregue una cantidad aleatoria de tiempo de suspensión que se superponga a la diferencia de tiempo entre los nombres de usuario existentes / no existentes.

es decir, si el escenario actual es,

A) Tiempo de respuesta del nombre de usuario existente = 50 ms

B) Tiempo de respuesta del nombre de usuario no existente = 120ms

Agregue 70-100 ms de tiempo de suspensión aleatorio a A (120-150 ms)

Agregue 0-30ms de tiempo de suspensión aleatorio a B (120-150ms).

    
respondido por el ferr 09.10.2015 - 21:13
fuente

Lea otras preguntas en las etiquetas