¿Es seguro exponer los hashes de contraseña de bcrypt?

12

Estoy creando una aplicación web que debe admitir el cambio de usuario fuera de línea y me pregunto si es seguro exponer los hash de bcrypt para autenticar a los usuarios.

El flujo básico:

  1. Los usuarios primarios inician sesión mientras la aplicación web está en línea, golpeando el servidor
  2. El servidor devuelve una lista de otros usuarios dentro de la misma compañía, incluidos sus hash de bcrypt (costo de hash de 10)
  3. Otro usuario puede crear un registro mientras la aplicación web está fuera de línea ingresando su propia contraseña, en comparación con las que se obtuvieron anteriormente utilizando una biblioteca JS bcrypt

La aplicación en sí misma es de bajo riesgo y no hay muchos escenarios en los que un usuario tenga incentivos para hacerse pasar por un compañero de trabajo, pero me gustaría hacerlo bien.

Gracias de antemano por cualquier información.

(Una pregunta de seguimiento es: al sincronizar con el servidor, cómo demostrarle al servidor que el otro usuario realmente realizó la autenticación sin conexión antes de crear su registro, pero eso podría ser mejor como su propia publicación).

    
pregunta Alex Dunae 25.11.2016 - 21:41
fuente

2 respuestas

32

No, no está bien, pero eso no es una cuestión de los hash bcrypt, sino porque movió la autenticación del servidor en el que confía (porque es su servidor) al dispositivo del usuario (en el que no puede confiar). Ahora se necesita un simple depurador para cambiar el resultado de "¿es válida esta contraseña?" marque de "no" a "sí".

Además, le da a un atacante un acceso persistente a una lista de hashes. Por lo tanto, el atacante puede forzar esa lista por el tiempo que quiera en cualquier computadora que quiera usar.

En pocas palabras, es una muy mala idea.

Lo correcto sería almacenar en caché las acciones del usuario que afirma para ser un compañero de trabajo de su usuario autenticado, y permitir que ese compañero de trabajo se autentique correctamente en su servidor tan pronto como vuelva. En línea para cometer realmente lo que hicieron en el servidor. Una forma de hacerlo es simplemente almacenar la contraseña que ingresó el usuario y luego enviarla al servidor, en cualquier forma que use durante un inicio de sesión "normal". Si eso funciona, está bien, el usuario se ha autenticado y ha almacenado Se pueden llevar a cabo acciones. Si no, bueno, dígale al usuario que ingresó una contraseña incorrecta y que sus acciones no se pueden cometer a menos que ingrese la correcta.

    
respondido por el Marcus Müller 25.11.2016 - 21:45
fuente
3

Al insertar hashes de contraseña en el cliente, los estaría exponiendo a ataques de fuerza bruta sin conexión. Eso puede ser completamente aceptable si las contraseñas son lo suficientemente fuertes. Pero esa es una suposición peligrosa.

Puede pedir a cada usuario que confirme que desea que se publique su propio hash (después de explicarles el riesgo). Pero hay muchos usuarios que con gusto dirían que entienden el riesgo y que han elegido una contraseña lo suficientemente fuerte, aunque lo que realmente eligieron fue password12345!!!

En este punto, tendrá que evaluar qué tan importante es esta característica en comparación con el riesgo de permitir que los usuarios se disparen en el pie.

Sin embargo, hay otro inconveniente en su diseño que es el de autenticar las actualizaciones realizadas fuera de línea. Confiar en el cliente para validar al usuario no es lo suficientemente bueno.

Almacenar la contraseña del usuario junto con la actualización y enviarla al servidor una vez en línea resolvería el problema de realizar solo la validación del lado del cliente. Pero introducirías tres nuevos problemas:

  1. No detectaría credenciales mal escritas al ingresar actualizaciones. Y más adelante, cuando el servidor verifique esas credenciales, es posible que el usuario que las escribió en primer lugar no pueda solucionar el problema.
  2. La actualización sería maleable. Sería trivial modificar la actualización mientras está almacenada en el dispositivo cliente para que realice una actualización diferente una vez que llegue al servidor.
  3. Estaría almacenando contraseñas en texto sin formato en el dispositivo, lo cual es un gran no-no hay seguridad.

No veo ninguna forma de evitar la fuerza bruta sin conexión de las contraseñas y al mismo tiempo permite que la contraseña se valide en el momento en que se ingresa una actualización, y esa validación es necesaria por razones de uso.

Por lo tanto, el enfoque que tomaría es crear un par de claves públicas basadas en la contraseña del usuario y un salt, y usar la clave privada correspondiente para firmar la actualización. Eso significa que la actualización ya no es maleable, y la contraseña no necesita ser almacenada y transmitida, sino que simplemente almacena y transmite la firma.

Por supuesto, el cliente todavía necesita saber sal y el nombre de usuario para generar una transacción firmada correctamente. Y tendría que conocer la clave pública o un hash para verificarla en primer lugar, lo que permite ataques de fuerza bruta sin conexión.

Puede truncar el lado del cliente verificado por hash a un solo byte, lo que hace que los ataques de fuerza bruta sin conexión sean menos factibles y aún tiene un 99% de posibilidades de detectar contraseñas incorrectas en el lado del cliente.

En el lado del servidor, todavía debe verificar la clave pública transmitida con un hash salado para asegurarse de que solo reciba transacciones válidas.

Obviamente, cualquier diseño para la aplicación que está diseñando deja mucho margen para errores, por lo que contar con el diseño y el código final revisados por múltiples profesionales de la seguridad es una necesidad para garantizar que el resultado final sea seguro.

    
respondido por el kasperd 26.11.2016 - 19:29
fuente

Lea otras preguntas en las etiquetas