Cómo hash largas contraseñas (> 72 caracteres) con blowfish

La semana pasada leí muchos artículos sobre el uso de contraseñas y Blowfish parece ser (uno de) el mejor algoritmo hash en este momento, pero ese no es el tema de esta pregunta.

El límite de 72 caracteres

Blowfish solo considera los primeros 72 caracteres en la contraseña ingresada:

 

El resultado es:

 string(119) "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)" string(72) "Wow. This is a super secret and super, super long password. Let's add so" bool(true) 

Como puede ver, solo importan los primeros 72 caracteres. Twitter está usando blowfish aka bcrypt para almacenar sus contraseñas ( https://shouldichangemypassword.com/twitter-hacked.php ) y adivina qué: cambia tu contraseña de Twitter a una contraseña larga con más de 72 caracteres y puedes iniciar sesión en tu cuenta ingresando solo los primeros 72 caracteres.

Blowfish y pimienta

Hay muchas opiniones diferentes sobre las contraseñas “pimienta”. Algunas personas dicen que es innecesario, porque debes asumir que el pimiento secreto también es conocido / publicado, por lo que no mejora el hash. Tengo un servidor de base de datos separado, así que es bastante posible que solo se filtre la base de datos y no el pimiento constante.

En este caso (la pimienta no se filtró) hace un ataque basado en un diccionario más difícil (corríjame si esto no está bien). Si tu pimiento también se filtró: no está tan mal, todavía tienes la sal y está tan bien protegida como un hash sin pimienta.

Así que creo que acribillar la contraseña es, al menos, una mala elección.

Sugerencia

Mi sugerencia de obtener un hash de Blowfish para una contraseña con más de 72 caracteres (y pimienta) es:

  

Esto se basa en esta pregunta : password_verify return false .

La pregunta

¿Cuál es la forma más segura? ¿Obtiene primero un hash SHA-256 (que devuelve 64 caracteres) o considera solo los primeros 72 caracteres de la contraseña?

Pros

  • El usuario no puede iniciar sesión ingresando solo los primeros 72 caracteres
  • Puedes agregar el pimiento sin exceder el límite de caracteres
  • La salida de hash_hmac probablemente tenga más entropía que la contraseña misma
  • La contraseña es hash por dos funciones diferentes

Contras

  • Solo 64 caracteres se usan para construir el hash blowfish

Edición 1: esta pregunta solo se refiere a la integración PHP de blowfish / bcrypt. ¡Gracias por los comentarios!

El problema aquí es básicamente un problema de entropía. Así que empecemos a buscar allí:

Entropía por personaje

La cantidad de bits de entropía por byte son:

  • Personajes hexagonales
    • Bits: 4
    • Valores: 16
    • Entropía en 72 caracteres: 288 bits
  • Alfanumérico
    • Bits: 6
    • Valores: 62
    • Entropía en 72 caracteres: 432 bits
  • Símbolos “comunes”
    • Bits: 6.5
    • Valores: 94
    • Entropía en 72 caracteres: 468 bits
  • Bytes completos
    • Bits: 8
    • Valores: 255
    • Entropía en 72 caracteres: 576 bits

Entonces, cómo actuamos depende del tipo de personajes que esperamos.

El primer problema

El primer problema con su código es que su paso “picante” está generando caracteres hexadecimales (ya que el cuarto parámetro para hash_hmac() no está establecido).

Por lo tanto, al mezclar su pimiento, está reduciendo efectivamente la entropía máxima disponible para la contraseña por un factor de 2 (de 576 a 288 bits posibles ).

El segundo problema

Sin embargo, sha256 solo proporciona 256 bits de entropía en primer lugar. Entonces, efectivamente está reduciendo un posible valor de 576 bits a 256 bits. Su hash step * inmediatamente *, por definición, pierde al menos el 50% de la entropía posible en la contraseña.

Podría resolverlo parcialmente cambiando a SHA512 , donde solo reduciría la entropía disponible en aproximadamente 12%. Pero esa sigue siendo una diferencia no insignificante. Ese 12% reduce el número de permutaciones por un factor de 1.8e19 . Eso es un gran número … Y ese es el factor que lo reduce por …

El problema subyacente

El problema subyacente es que hay tres tipos de contraseñas de más de 72 caracteres. El impacto que este sistema de estilo tiene sobre ellos será muy diferente:

Nota: a partir de ahora supongo que estamos comparando con un sistema de pimienta que usa SHA512 con salida en bruto (no hexadecimal).

  • Contraseñas aleatorias de alta entropía

    Estos son sus usuarios que usan generadores de contraseñas que generan lo que equivalen a claves grandes para contraseñas. Son aleatorios (generados, no elegidos por el ser humano) y tienen alta entropía por personaje. Estos tipos usan bytes altos (caracteres> 127) y algunos caracteres de control.

    Para este grupo, su función de hash reducirá significativamente su entropía disponible en bcrypt .

    Déjame decirlo de nuevo. Para los usuarios que usan contraseñas largas de alta entropía, su solución reduce significativamente la fortaleza de su contraseña en una cantidad medible. (62 bits de entropía perdidos para una contraseña de 72 caracteres, y más para contraseñas más largas)

  • Contraseñas aleatorias de entropía media

    Este grupo usa contraseñas que contienen símbolos comunes, pero no altos bytes o caracteres de control. Estas son sus contraseñas tipables.

    Para este grupo, desbloqueará un poco más la entropía (no la creará, pero permitirá que entre más entropía en la contraseña de brypt). Cuando digo un poco, quiero decir un poco. El punto de equilibrio ocurre cuando maximiza los 512 bits que tiene SHA512. Por lo tanto, el pico tiene 78 caracteres.

    Déjame decirlo de nuevo. Para esta clase de contraseñas, solo puede almacenar 6 caracteres adicionales antes de que se quede sin entropía.

  • Contraseñas de baja entropía no aleatorias

    Este es el grupo que usa caracteres alfanuméricos que probablemente no se generen al azar. Algo así como una cita de la Biblia o tal. Estas frases tienen aproximadamente 2.3 bits de entropía por personaje.

    Para este grupo, puede desbloquear significativamente más entropía (no crearlo, pero permitir que más encaje en la entrada de contraseña de brypt) mediante hash. El punto de equilibrio tiene alrededor de 223 caracteres antes de que se quede sin entropía.

    Digamos eso de nuevo. Para esta clase de contraseñas, el hashing previo definitivamente aumenta la seguridad significativamente.

De vuelta al mundo real

Este tipo de cálculos de entropía realmente no importan mucho en el mundo real. Lo que importa es adivinar la entropía. Eso es lo que afecta directamente lo que los atacantes pueden hacer. Eso es lo que quieres maximizar.

Aunque hay poca investigación que se haya dedicado a adivinar la entropía, hay algunos puntos que me gustaría señalar.

Las posibilidades de adivinar aleatoriamente 72 caracteres correctos seguidos son extremadamente bajas. Es más probable que ganes la lotería Powerball 21 veces, que tener esta colisión … Así de grande es la cantidad de la que estamos hablando.

Pero no podemos tropezar con esto estadísticamente. En el caso de las frases, la probabilidad de que los primeros 72 caracteres sean iguales es mucho mayor que la de una contraseña aleatoria. Pero todavía es trivialmente bajo (es más probable que ganes la lotería Powerball 5 veces, en base a 2.3 bits por personaje).

Prácticamente

Prácticamente, realmente no importa. Las posibilidades de que alguien adivine correctamente los primeros 72 caracteres, donde los segundos marcan una diferencia significativa, son tan bajos que no vale la pena preocuparse. ¿Por qué?

Bueno, digamos que estás tomando una frase. Si la persona puede obtener los primeros 72 caracteres correctos, son realmente afortunados (no probable), o es una frase común. Si es una frase común, la única variable es cuánto tiempo demorarla.

Tomemos un ejemplo. Tomemos una cita de la Biblia (solo porque es una fuente común de texto largo, no por ninguna otra razón):

No codiciarás la casa de tu prójimo. No codiciarás la mujer de tu prójimo, ni su siervo, ni su sierva, ni su buey ni su asno, ni nada que le pertenezca a tu prójimo.

Eso es 180 caracteres. El personaje 73 es el g en el neighbor's . Si adivinaste mucho, es probable que no te detengas en nei , sino que continúes con el rest del versículo (ya que así es como es probable que se use la contraseña). Por lo tanto, su “hash” no agregó mucho.

Por cierto: ABSOLUTAMENTE NO estoy abogando por usar una cita bíblica. De hecho, todo lo contrario.

Conclusión

Realmente no vas a ayudar a las personas que usan contraseñas largas al hacer hash primero. Algunos grupos definitivamente puedes ayudar. Algunos definitivamente puedes lastimar.

Pero al final, nada de eso es demasiado significativo. Los números con los que nos enfrentamos son MUY demasiado altos. La diferencia en entropía no va a ser mucho.

Es mejor que dejes bcrypt como está. Es más probable que arruines el hashing (literalmente, ya lo has hecho, y no eres el primero, ni el último en cometer ese error) de lo que el ataque que intentas evitar sucederá.

Concéntrese en asegurar el rest del sitio. Y agregue un contador de entropía de contraseña al cuadro de contraseña en el registro para indicar la fortaleza de la contraseña (e indique si la contraseña es demasiado larga para que el usuario desee cambiarla) …

Esa es mi $ 0.02 al menos (o posiblemente mucho más de $ 0.02) …

En cuanto a usar un pimiento “secreto”:

Literalmente, no hay investigaciones sobre la alimentación de una función hash en bcrypt. Por lo tanto, no está claro en el mejor de los casos si alimentar un hash “pimentado” en brypt causará vulnerabilidades desconocidas (sabemos que hacer hash1(hash2($value)) puede exponer vulnerabilidades significativas alrededor de la resistencia a colisiones y ataques de preimagen).

Teniendo en cuenta que ya está considerando almacenar una clave secreta (el “pimiento”), ¿por qué no utilizarla de una manera que esté bien estudiada y comprendida? ¿Por qué no encriptar el hash antes de almacenarlo?

Básicamente, después de hash la contraseña, alimenta toda la salida de hash a un fuerte algoritmo de cifrado. Luego almacene el resultado encriptado.

Ahora, un ataque de inyección SQL no filtrará nada útil, porque no tienen la clave de cifrado. Y si se filtra la llave, los atacantes no están mejor que si usaras un hash simple (que es demostrable, algo con el “pre-hash” de pimienta no proporciona).

Nota: si elige hacer esto, use una biblioteca. Para PHP, recomiendo encarecidamente el paquete Zend\Crypt Zend Framework 2. En realidad, es el único que recomendaría en este momento actual. Ha sido muy revisado, y toma todas las decisiones por ti (lo cual es algo muy bueno) …

Algo como:

 use Zend\Crypt\BlockCipher; public function createHash($password) { $hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]); $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes')); $blockCipher->setKey($this->key); return $blockCipher->encrypt($hash); } public function verifyHash($password, $hash) { $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes')); $blockCipher->setKey($this->key); $hash = $blockCipher->decrypt($hash); return password_verify($password, $hash); } 

Y es beneficioso porque está utilizando todos los algoritmos de maneras que se entienden bien y se estudian bien (al menos al menos). Recuerda:

Cualquiera, desde el aficionado más despistado hasta el mejor criptógrafo, puede crear un algoritmo que él mismo no puede descifrar.

  • Bruce Schneier

Las contraseñas salteadas seguramente son algo bueno que hacer, pero veamos por qué.

Primero debemos responder la pregunta cuando ayuda exactamente un pimiento. El pimiento solo protege las contraseñas, siempre que se mantenga en secreto, por lo que si un atacante tiene acceso al servidor, no sirve de nada. Aunque un ataque mucho más fácil es la inyección SQL, que permite el acceso de lectura a la base de datos (a nuestros valores hash), preparé una demostración de inyección SQL para mostrar qué tan fácil puede ser (haga clic en la flecha siguiente para obtener un entrada).

Entonces, ¿qué es lo que realmente ayuda el pimiento? Mientras el pimiento permanezca en secreto, protege las contraseñas débiles de un ataque de diccionario. La contraseña 1234 se convertiría en algo así como 1234-p*deDIUZeRweretWy+.O . Esta contraseña no solo es mucho más larga, también contiene caracteres especiales y nunca será parte de ningún diccionario.

Ahora podemos estimar qué contraseñas usarán nuestros usuarios, probablemente más usuarios ingresen contraseñas débiles, ya que hay usuarios con contraseñas entre 64 y 72 caracteres (en realidad, esto será muy raro).

Otro punto es el rango de fuerza bruta. La función de hash sha256 devolverá 256 bits de salida o 1.2E77 combinaciones, eso es demasiado para fuerza bruta, incluso para GPU (si calculé correctamente, esto necesitaría alrededor de 2E61 años en una GPU en 2013). Entonces no tenemos una desventaja real al aplicar el pimiento. Debido a que los valores de hash no son sistemáticos, no se puede acelerar la fuerza bruta con patrones comunes.

PD: Hasta donde sé, el límite de 72 caracteres es específico del algoritmo de BCrypt. La mejor respuesta que encontré es esta .

PPS Creo que su ejemplo es defectuoso, no puede generar el hash con la longitud completa de la contraseña y verificarlo con uno truncado. Probablemente quieras aplicar el pimiento de la misma manera para generar el hash y para verificar el hash.

Bcrypt usa un algoritmo basado en el costoso algoritmo de configuración de clave Blowfish.

El límite recomendado de contraseña de 56 bytes (incluido el byte de terminación nulo) para bcrypt se relaciona con el límite de 448 bits de la clave Blowfish. Cualquier byte más allá de ese límite no se mezcla completamente en el hash resultante. Por lo tanto, el límite absoluto de 72 bytes en las contraseñas de bcrypt es menos relevante, cuando se considera el efecto real en el hash resultante por esos bytes.

Si cree que sus usuarios normalmente elegirían contraseñas de más de 55 bytes de longitud, recuerde que siempre puede boost las rondas de extensión de contraseñas, para boost la seguridad en el caso de una violación de la tabla de contraseñas (aunque esto tiene que ser mucho comparado con agregar caracteres). Si los derechos de acceso de los usuarios son tan importantes que los usuarios normalmente requerirían una contraseña masivamente larga, la caducidad de la contraseña también debería ser corta, como 2 semanas. Esto significa que es mucho menos probable que una contraseña siga siendo válida mientras un pirata informático invierte sus recursos en la derrota del factor de trabajo involucrado en probar cada contraseña de prueba para ver si produce un hash coincidente.

Por supuesto, en el caso de que no se viole la tabla de contraseñas, solo deberíamos permitir a los piratas informáticos, a lo sumo, diez bashs de adivinar la contraseña de 55 bytes de un usuario, antes de bloquear la cuenta del usuario;)

Si decide preconfigurar una contraseña de más de 55 bytes, debe usar SHA-384, ya que tiene el mayor rendimiento sin sobrepasar el límite.