¿Cómo puedo asegurarme de que el hash de contraseñas esté seguro en las computadoras mientras no sea prohibitivo en los dispositivos móviles?

15

Soy nuevo en criptografía y sus implementaciones. Estoy diseñando una aplicación para Android donde un usuario ingresa una contraseña para recuperar algunos datos encriptados. Después de algunas investigaciones sobre posibles soluciones, terminé con bcrypt utilizado como tal:

  

El usuario ingresa la contraseña - > bcrypt (Contraseña) - > clave para el cifrado

Y luego almacenándolo en la base de datos como tal:

  

SHA256 (bcrypt (contraseña)) - > verificación del usuario (esto se almacena en DB)

Estaba planeando usar la implementación jBCrypt en Android, pero por lo que he leído bcrypt es extremadamente lento en teléfonos móviles ( over minutes dependiendo del modelo del teléfono / nivel seleccionado .)

Sé que desacelerar el procedimiento de hash es el núcleo del algoritmo bcrypt, pero no es aceptable que un usuario requiera 2 minutos para iniciar sesión / descifrar en un dispositivo móvil, mientras que la misma operación en una PC normal tarda segundos. Sin embargo, si reduzco el recuento de iteraciones en el teléfono inteligente, un atacante puede atacar con fuerza bruta en una PC más rápida. ¿Cómo puedo reconciliar estos dos aspectos del problema?

EDITAR

Finalmente decidí probar jBCrypt en Android, y lo probé en varios teléfonos móviles que tengo con diferentes versiones de SO. También lo probé en la PC de mi casa y compilé una tabla de resultados:

Parece que un Factor de trabajo de 10-11 sería aceptable y aún así sería razonablemente seguro. Intentaré implementar una solución NDK para ver si puedo mejorar los resultados. (Voy a publicar resultados si termino haciendo pruebas).

    
pregunta nsL 11.09.2013 - 15:06
fuente

4 respuestas

10

jBcrypt es una implementación de Java. Las aplicaciones de Android están escritas en casi Java, para una máquina virtual casi Java llamada Dalvik .

Para tareas intensivas en computación, especialmente algoritmos criptográficos, Java incurre en una desaceleración típica de 3x en comparación con el código C equivalente optimizado: una buena máquina virtual Java ejecuta un compilador JIT , y el rendimiento del código estará limitado por las restricciones en las que debe funcionar una JVM (la traducción del código de bytes al código nativo debe ser rápida e incremental, los métodos se traducen por llamada) ; y el resultado todavía debe ser apropiado para el recolector de basura ). Se puede observar una mayor desaceleración en algunos casos específicos donde el código nativo puede beneficiarse de los códigos de operación adicionales proporcionados por la CPU, siendo el caso típico los cálculos sobre grandes enteros (por ejemplo, para RSA), porque la CPU puede ofrecer una multiplicación "extendida" (64x64- > 128) que el código Java no puede usar, debido a la falta de un tipo de entero de 128 bits en Java.

Para un experimento bastante completo en el contexto de las funciones hash (no "hashing de contraseña", simplemente "hashing"), consulte sphlib : una biblioteca que implementa muchas funciones hash, en C y en Java, con "esfuerzos similares en optimizaciones" (el mismo desarrollador para todo el lote), e incluye medidas en una variedad de tipos de plataformas .

Ahora, puede haber ralentizaciones adicionales para las aplicaciones de Android:

  • La CPU de un teléfono inteligente no es tan rápida como la de una PC. La CPU del teléfono inteligente puede ir al rango de gigahercios, pero aún así hacer menos trabajo por ciclo que la CPU x86, porque uno de los principales objetivos de una CPU de teléfono inteligente es ahorrar batería. Un teléfono inteligente debe estar activo durante un día completo, mientras participa en la actividad de la radio, mientras que una computadora portátil puede tener una batería más voluminosa y solo necesita estar encendida durante algunas horas. Los teléfonos inteligentes modernos son eficientes, pero todavía hay una brecha en comparación con la PC de escritorio. Digamos un factor 3x adicional.

  • Dalvik no es necesariamente el mejor compilador JIT del mundo. La relativa falta de músculo en bruto significa que el JIT no puede aplicar las estrategias de optimización más complejas, ya que haría la traducción JIT de un método demasiado costoso.

  • Hasta Android 2.1 (incluido), Dalvik no tiene JIT . Esta es la peor desaceleración, a la que aluden otras respuestas. En Android 2.1 (y antes), el bytecode de Java se interpreta, no se traduce a código nativo, y esto conlleva un alto costo adicional (por ejemplo, 10 a 20 veces más lento que el código JIT). Android 2.2 en adelante tiene JIT.

Cambiar a PBKDF2 no cambiará mucho la imagen. Si usa PBKDF2 , asegúrese de usar SHA-256 (o SHA-1), no SHA-512, porque la CPU del teléfono inteligente está basada en ARM y será bastante incómoda con las operaciones aritméticas de 64 bits en las que SHA-512 depende en gran medida, lo que ralentizará su código, mientras que el atacante tiene una PC, que realiza operaciones de 64 bits para el desayuno No incurrir en la misma desaceleración.

Resumen: bcrypt será muy lento en Android 2.1 (y versiones anteriores). Será algo rápido en Android 2.2, aunque no tan rápido como una PC (por ejemplo, entre 5 y 10 veces más lento). PBKDF2, scrypt ... no cambie esta imagen de manera significativa.

Todo esto se dice sobre el rendimiento de bcrypt. No hice ningún comentario sobre el uso correcto de los secretos derivados de bcrypt para la verificación de la contraseña y del cifrado. De hecho, cuando almacena "algo" para validar una contraseña, no hace que ese "algo" esté demasiado cerca de la clave derivada de la contraseña que utiliza para el cifrado.

Un "método criptográficamente razonable" es calcular el hash de contraseña con bcrypt o PBKDF2 (con sales e iteraciones y todo), y luego tomar la salida y expandirla con a Key Derivation Function en suficiente material clave para sus necesidades. En su contexto, tome la salida de bcrypt, luego cáscara con SHA-256. Esto produce 256 bits. Almacene los primeros 128 bits en su base de datos como "token de verificación de contraseña". Utilice la otra mitad como clave de cifrado.

PBKDF2 hace que este proceso sea un poco más sencillo ya que es un KDF, por lo que puede solicitar 256 bits de salida derivada de contraseña de inmediato, sin necesidad de un hashing adicional; mientras que el tamaño de salida de bcrypt se fija en 192 bits, lo que puede no ser suficiente para sus necesidades.

    
respondido por el Tom Leek 11.09.2013 - 15:44
fuente
1

He estado en la misma situación. En Android deberías poder usar PBKDF2 cómodamente. No he probado scrypt en un dispositivo móvil, pero espero que el rendimiento no sea mejor que scrypt cuando se genera un hash. Aparte de eso, en lugar de tomar un SHA-256 , podría simplemente usar PBKDF2 con un SHA-256 HMAC . Recuerde que PBKDF2 también le permite establecer la longitud de salida del hash (preferiblemente, use HKDF-Extract en la salida de PBKDF2 en lugar de ). Así que normalmente tomar el SHA-256(pbkdf2(pass)) será demasiado.

Sin embargo, recomendaría que tomes otro PBKDF2(PKBDF2(pass)) y lo guardes en tu aplicación también. De esa manera, puede verificar que, cuando el usuario ingresa una contraseña y usa PBKDF2 para generar la clave (interior PBKDF2 ), esa clave es la clave correcta antes de descifrarla.

Entonces:

  • password = > utilizado para generar clave (secreto, no debe ser almacenado)
  • PBKDF2(password) = > se utiliza como clave para descifrar sus datos cifrados (secreto, no debe almacenarse)
  • PBKDF2(PBKDF2(password)) = > se utiliza para verificar la clave antes de intentar el descifrado (no secreto, se puede almacenar dentro de su aplicación)
respondido por el Lucas Kauffman 11.09.2013 - 15:25
fuente
1

Es el mismo dilema al configurar un administrador de contraseñas como KeePass en múltiples plataformas: ¿en qué plataforma se enfoca?

Debido a que bcrypt es una función de derivación clave utilizada para extender el proceso rápido de paso a través de contraseñas humanas predecibles; Si se pasa por alto el problema de las contraseñas humanas, se pasa por alto el problema de rendimiento.

Tener acceso a los datos regidos por una clave de sesión. Cuando es necesario regenerar la clave de la sesión (por ejemplo, cada 24 horas o después de reiniciar el teléfono) se usa bcrypt(password) o bcrypt(password + master key) . Durante una sesión, la clave de la sesión solo se protege mediante el cifrado de contraseñas no cifradas para la velocidad.

Alternativamente, puede dejar el bcrypt() y el retraso asociado en su lugar y simplemente mejorar la experiencia de la interfaz de usuario para el proceso.

Si el proceso de descifrado se ejecuta como una tarea de fondo con límite de CPU con una notificación en primer plano que marca el estado del descifrado con la barra de progreso y el porcentaje; la mayoría de los usuarios lo considerarán aceptable si el objetivo principal de la aplicación es la seguridad. De hecho, incluso podría hacerles sentir que la aplicación es más segura de lo que lo haría un descifrado súper rápido.

Por supuesto, si el propósito principal de la aplicación no está relacionado con la seguridad, es posible que desee volver a visitarla si realmente necesita la mejora de seguridad de bcrypt() y el retraso asociado del dispositivo móvil.

    
respondido por el LateralFractal 11.09.2013 - 15:38
fuente
0

Como reconoces, tienes requisitos contradictorios. Desea que bcrypt sea lo más costoso posible para ayudar a defenderse contra crackers fuera de línea, mientras que al mismo tiempo desea que no sea prohibitivamente costoso para el usuario legítimo en un dispositivo de potencia relativamente baja.

Hay dos tipos de enfoques, los cuales deberías seguir. Ajuste su factor de costo (o iteraciones) y encuentre una implementación de hash que le ofrezca la mejor proporción de atacante / defensor (la más pequeña).

Reducir la proporción de atacante / defensor.

El cracker de contraseñas, Bitweisel, habla sobre lo que él llama la "relación atacante / defensor" (ADR) . Cuando el Defender está atascado ejecutando bcrypt, scrypt o PBKDF2 de una manera que es, digamos, 1/10 de la velocidad de lo que el atacante tiene disponible, entonces el ADR es de 10 a 1. En su situación, el Defender no está solo en un dispositivo menos potente, también puede estar atascado con compiladores que no funcionan bien en estos sistemas.

No sé lo suficiente sobre Android como para indicarle la dirección correcta, pero debería intentar encontrar algo cuidadosamente optimizado para su entorno operativo. Si eso será bcrypt o PBKDF2, no lo sé. Nuevamente, no estás preocupado (aún) por la velocidad absoluta, sino por la proporción entre tu entorno y el atacante.

Por ejemplo, puede ser que PBKDF2 que usa HMAC-SHA512 sea extremadamente lento para usted, pero eso puede valer la pena si el uso de SHA512 impide la capacidad del atacante para usar GPU para el craqueo. Así que aunque sea lento, esto podría hacerte con el ADR perdido.

Instale una copia de ocl-hashcat + (el cracker que está en ayunas) para probar las tasas de craqueo en su computadora de escritorio para que tendrá ese lado de la ecuación para juzgar el ADR para diferentes esquemas de hash.

Ajuste su factor de costo

PBKDF2, bcrypt y scrypt le permiten establecer factores de trabajo. La declaración "bcrypt tarda dos minutos" se puede corregir reduciendo el costo del parámetro en uno (para que tome un minuto) o en dos (durante 30 segundos). Con PBKDF2 se establece la iteración, que se escala linealmente.

Entonces, una vez que haya encontrado el algoritmo y la implementación que le permiten obtener el mejor ADR, simplemente decida a qué hora tolerarán los usuarios. Si son 2 segundos, ajuste el factor de costo o las iteraciones para obtener esos dos minutos.

    
respondido por el Jeffrey Goldberg 12.09.2013 - 07:39
fuente

Lea otras preguntas en las etiquetas