PHP crypt () recorta la sal ya que sería demasiado largo

10

Estoy usando Blowfish con PHP crypt () para el hashing de contraseñas, pero noté algo extraño. Citando documentación de PHP:

  

CRYPT_BLOWFISH - Blowfish hashing con una sal de la siguiente manera: "$ 2a $",   "$ 2x $" o "$ 2y $", un parámetro de costo de dos dígitos, "$" y 22 dígitos de   el alfabeto "./0-9A-Za-z".

Noté que la sal que se incluye en el hash final es de 1 carácter corto (el último se corta) como si la sal fuera demasiado larga, pero ese no es el caso.

Ejemplo de salida de mi script:

  

Sal: 97504ebb48c4619f820f83 con longitud 22

     

Blowfish: $ 2a $ 13 $ 97504ebb48c4619f820f8u4QTtlV5MoqHt9l7hmK4jEohUXrI.0PK

     

Hash match.

Como puede ver, la sal aleatoria tiene exactamente 22 dígitos, pero el '3' falta en el hash final. Si hago los caracteres salt 21 solo obtengo un hash dañado y no funciona. Entonces, ¿por qué recorta el último carácter?

Los ejemplos en manual de PHP también agregan un $ final a la sal aleatoria. ¿Es ese $ por una razón o simplemente lo agregaron al azar a Blowfish, SHA-256 y SHA-512 para confundir a todos?

Y finalmente, este es mi código:

if (CRYPT_BLOWFISH == 1) {
    $salt = md5(uniqid(rand(), TRUE));
    $salt = substr($salt, 0, 22);

    echo "Salt: " . $salt . " with length " . strlen($salt) . "<br />";
    $pass = "rasmuslerdorf";

    $bsalt = "$2a$13$".$salt;
    $blowfish=crypt($pass, $bsalt);
    echo 'Blowfish:     ' . $blowfish . "<br />";

    if (crypt($pass, $blowfish) == $blowfish) {
        echo "Hash match.<br />";
    }
    else echo "no<br />";
}
else {
    exit("You need php 5.3 or newer");
}
    
pregunta cen 30.09.2012 - 15:48
fuente

1 respuesta

18

En realidad, el '3' está ahí, pero se llama 'u'.

Explicaciones: bcrypt necesita un salt de 128 bits. La sal que proporcione debe estar en "base64 modificado", es decir, que consta de letras, dígitos, '/' o '.' signos (esto está "modificado" porque en verdadero Base64 , se usa el signo '+' en lugar de '.', y el orden es diferente) Dado que se trata de un alfabeto de 64 elementos, cada carácter vale 6 bits y sus 22 caracteres codifican 132 bits.

Bcrypt solo usa los primeros 128 bits de tus 132 bits, por lo que los últimos cuatro bits se ignoran por completo. Los últimos cuatro bits son los últimos cuatro bits del último carácter de sal. En la base64 modificada, 'u' tiene valor 48, 110000 en binario, mientras que '3' tiene valor 57, 111001 en binario. Como puede ver, los dos valores solo se diferencian en sus últimos cuatro bits (más a la derecha), que se ignoran.

Lo que sucede es que la implementación de bcrypt que usa primero convierte su sal (22 caracteres) en un búfer de 128 bits (16 bytes), y ahí es donde ocurre la caída. Luego, el código lo convertirá atrás a base64 modificado, pero esta vez usará ceros para los "cuatro bits que faltan", convirtiendo su '3' en una 'u'. Si usa '97504ebb48c4619f820f8u' en lugar de '97504ebb48c4619f820f83' como sal, encontrará que obtiene exactamente la misma salida de bcrypt, porque estas dos sales solo se diferencian en los últimos cuatro bits, que se ignoran.

    
respondido por el Thomas Pornin 30.09.2012 - 16:25
fuente

Lea otras preguntas en las etiquetas