2010-09-29 11 views
17

He estado usando PHP crypt() como una forma de almacenar y verificar contraseñas en mi base de datos. Utilizo hashing para otras cosas, pero crypt() para contraseñas. La documentación no es tan buena y parece que hay mucho debate. Estoy usando blowfish y dos sales para cifrar una contraseña y almacenarla en la base de datos. Antes almacenaba la sal y la contraseña cifrada (como un hash salado) pero me di cuenta de que era redundante porque el salt formaba parte de la cadena de contraseña cifrada.¿Estoy usando correctamente la función crypt() de PHP?

Estoy un poco confundido sobre cómo funcionarían los ataques de tabla de arco iris en crypt(), de todos modos, esto parece correcto desde un punto de vista de seguridad. Uso una segunda sal para agregar a la contraseña para aumentar la entropía de las contraseñas cortas, probablemente exagerada, pero ¿por qué no?

function crypt_password($password) { 
if ($password) { 
    //find the longest valid salt allowed by server 
    $max_salt = CRYPT_SALT_LENGTH; 

    //blowfish hashing with a salt as follows: "$2a$", a two digit cost parameter, "$", and 22 base 64 
    $blowfish = '$2a$10$'; 

    //get the longest salt, could set to 22 crypt ignores extra data 
    $salt = get_salt ($max_salt); 

    //get a second salt to strengthen password 
    $salt2 = get_salt (30); //set to whatever 


    //append salt2 data to the password, and crypt using salt, results in a 60 char output 
    $crypt_pass = crypt ($password . $salt2, $blowfish . $salt); 

    //insert crypt pass along with salt2 into database. 
    $sql = "insert into database...."; 

    return true; 
    } 
} 


function get_salt($length) { 
$options = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./'; 

$salt = ''; 

for($i = 0; $i <= $length; $i ++) { 
    $options = str_shuffle ($options); 
    $salt .= $options [rand (0, 63)]; 
} 
return $salt; 
} 

function verify_password($input_password) 
{ 
if($input_password) 
{ 
    //get stored crypt pass,and salt2 from the database 
    $stored_password = 'somethingfromdatabase'; 
    $stored_salt2 = 'somethingelsefromdatabase'; 

    //compare the crypt of input+stored_salt2 to the stored crypt password 
    if (crypt($input_password . $stored_salt2, $stored_password) == $stored_password) { 
     //authenticated 
     return true; 
    } 
    else return false; 
} 
else return false; 
} 
+0

Usar 'mt_rand' en lugar de' rand' sería una pequeña mejora de su script – Sliq

Respuesta

15

Deberías echarle un vistazo a PHPASS: http://www.openwall.com/phpass/ Es un marco hashing de contraseñas que usa crypt() que se usa en proyectos como Wordpress y phpBB.

También hay un excelente artículo sobre este sitio web sobre hash de la clave, la salazón y se extiende utilizando la cripta(): http://www.openwall.com/articles/PHP-Users-Passwords

ACTUALIZACIÓN: Actualmente hay una alternativa para la biblioteca PHPASS. En la próxima versión de PHP hay funciones especiales para hash y verificación de contraseñas (usando bcrypt): http://www.php.net/manual/en/ref.password.php. Existe una biblioteca de compatibilidad que implementa estas funciones para PHP 5.3.7+: https://github.com/ircmaxell/password_compat

+1

Veo cómo ese marco puede ser útil para un enfoque directo, pero no veo mucha diferencia entre mi código y el código del código phpass. – Brian

+1

Y: PHPass es un desastre. Un desastre masivo. Es tan complicado que no confiaría en eso. – Sliq

+0

@Panique te gustaría explicar? –

3

La idea de una tabla de arcoiris es que un atacante puede hacer una tabla con todas las contraseñas posibles y sus hashes en el hogar.

E.g.

PASSWORD HASH 
iloveSO gjroewjgo 
password knbnogjwm 
secret gjroehghe 
jbieber rewgroewj 

etc.

Con esta tabla, el atacante puede convertir rápidamente cualquier hash para una contraseña. La tabla Rainbow usa algunos trucos para que no se almacenen todos los hashes, pero aún calcula todos los hashes de antemano.

Al usar una sal, incluso cuando la almacena con la contraseña, esto hace que sea mucho más difícil. En lugar de codificar cada palabra en un diccionario, el atacante tendría que ajustar cada palabra con cada sal. Con una sal suficientemente larga, esto proporciona suficientes combinaciones para que no sea factible calcular todos estos hashes.

Así que una sal no pretende ser una contraseña adicional, conocida solo por la aplicación, está destinada a cambiar la función hash para que no sea estándar.

+0

Está usando una sal, pero es de una longitud desconocida. Con un valor de sal significativamente grande, una tabla de arcoíris sería inviable. Por ejemplo 256^256, o una sal de base 256 de 256 bytes es tan grande que ni siquiera tenemos una palabra para esa cantidad de bytes necesarios para descifrar una contraseña de un solo carácter. – rook

2

Este es un uso indebido de crypt() porque está utilizando una primitiva obsoleta. Blowfish es muy viejo, doble es el reemplazo e incluso eso es viejo porque Threefish casi está finalizado. Deberías estar usando un miembro de la familia sha2, sha256 o sha512 son buenas opciones. crypt() se puede usar con sha256 o sha512, debe usar los parámetros CRYPT_SHA256 CRYPT_SHA512 respectivamente.

También sus sales tienen una relación de entropía/tamaño muy pequeña, solo está utilizando un conjunto alfanumérico, lo que es una broma porque las tablas alfanuméricas de arcoíris son las más comunes. Deberías usar un byte completo que base256, y yo recomiendo una sal de 256 bytes de longitud. Tenga en cuenta que todas las funciones hash son binarias seguras por definición, por lo que no debería preocuparse por bytes nulos y similares.

+0

Blowfish solo puede tomar caracteres alfanuméricos, así que tal vez debería usar CRYPT_SHA512. – Brian

+0

@Brian Perin, entonces eso es algo extraño con crypt() porque las cifras de bloque se usan para encriptar archivos y tráfico todo el tiempo. – rook

+2

El Blowfish utilizado por 'crypt()' no es exactamente igual al cifrado de bloques Blowfish de antaño: es un algoritmo de variante creado específicamente para el descifrado de claves/derivación de claves. – caf

11

Su uso de crypt() está bien. crypt($input, $stored) == $stored es la forma en que está diseñado para ser utilizado.

Su función get_salt() no es excelente, ya que utiliza la función rand(), a menudo pobre. En su lugar, debería considerar usar una función aleatoria más fuerte, como openssl_random_pseudo_bytes().

0

Utilice SHA-512 (si está disponible) con una sal que incluya time() y openssl_random_pseudo_bytes(). Crypt es consolidado/eficiente porque devuelve la sal insertada con la cadena hash.

Cuestiones relacionadas