password_verify password_hash password_bcrypt password online ejemplo desencriptar crypt_blowfish php salt password-hash php-password-hash

php - password_bcrypt - Cómo usar password_hash



password_verify php (7)

Código completo de contraseña de clase:

Class Password { public function __construct() {} /** * Hash the password using the specified algorithm * * @param string $password The password to hash * @param int $algo The algorithm to use (Defined by PASSWORD_* constants) * @param array $options The options for the algorithm to use * * @return string|false The hashed password, or false on error. */ function password_hash($password, $algo, array $options = array()) { if (!function_exists(''crypt'')) { trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING); return null; } if (!is_string($password)) { trigger_error("password_hash(): Password must be a string", E_USER_WARNING); return null; } if (!is_int($algo)) { trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING); return null; } switch ($algo) { case PASSWORD_BCRYPT : // Note that this is a C constant, but not exposed to PHP, so we don''t define it here. $cost = 10; if (isset($options[''cost''])) { $cost = $options[''cost'']; if ($cost < 4 || $cost > 31) { trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING); return null; } } // The length of salt to generate $raw_salt_len = 16; // The length required in the final serialization $required_salt_len = 22; $hash_format = sprintf("$2y$%02d$", $cost); break; default : trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING); return null; } if (isset($options[''salt''])) { switch (gettype($options[''salt''])) { case ''NULL'' : case ''boolean'' : case ''integer'' : case ''double'' : case ''string'' : $salt = (string)$options[''salt'']; break; case ''object'' : if (method_exists($options[''salt''], ''__tostring'')) { $salt = (string)$options[''salt'']; break; } case ''array'' : case ''resource'' : default : trigger_error(''password_hash(): Non-string salt parameter supplied'', E_USER_WARNING); return null; } if (strlen($salt) < $required_salt_len) { trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", strlen($salt), $required_salt_len), E_USER_WARNING); return null; } elseif (0 == preg_match(''#^[a-zA-Z0-9./]+$#D'', $salt)) { $salt = str_replace(''+'', ''.'', base64_encode($salt)); } } else { $salt = str_replace(''+'', ''.'', base64_encode($this->generate_entropy($required_salt_len))); } $salt = substr($salt, 0, $required_salt_len); $hash = $hash_format . $salt; $ret = crypt($password, $hash); if (!is_string($ret) || strlen($ret) <= 13) { return false; } return $ret; } /** * Generates Entropy using the safest available method, falling back to less preferred methods depending on support * * @param int $bytes * * @return string Returns raw bytes */ function generate_entropy($bytes){ $buffer = ''''; $buffer_valid = false; if (function_exists(''mcrypt_create_iv'') && !defined(''PHALANGER'')) { $buffer = mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM); if ($buffer) { $buffer_valid = true; } } if (!$buffer_valid && function_exists(''openssl_random_pseudo_bytes'')) { $buffer = openssl_random_pseudo_bytes($bytes); if ($buffer) { $buffer_valid = true; } } if (!$buffer_valid && is_readable(''/dev/urandom'')) { $f = fopen(''/dev/urandom'', ''r''); $read = strlen($buffer); while ($read < $bytes) { $buffer .= fread($f, $bytes - $read); $read = strlen($buffer); } fclose($f); if ($read >= $bytes) { $buffer_valid = true; } } if (!$buffer_valid || strlen($buffer) < $bytes) { $bl = strlen($buffer); for ($i = 0; $i < $bytes; $i++) { if ($i < $bl) { $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255)); } else { $buffer .= chr(mt_rand(0, 255)); } } } return $buffer; } /** * Get information about the password hash. Returns an array of the information * that was used to generate the password hash. * * array( * ''algo'' => 1, * ''algoName'' => ''bcrypt'', * ''options'' => array( * ''cost'' => 10, * ), * ) * * @param string $hash The password hash to extract info from * * @return array The array of information about the hash. */ function password_get_info($hash) { $return = array(''algo'' => 0, ''algoName'' => ''unknown'', ''options'' => array(), ); if (substr($hash, 0, 4) == ''$2y$'' && strlen($hash) == 60) { $return[''algo''] = PASSWORD_BCRYPT; $return[''algoName''] = ''bcrypt''; list($cost) = sscanf($hash, "$2y$%d$"); $return[''options''][''cost''] = $cost; } return $return; } /** * Determine if the password hash needs to be rehashed according to the options provided * * If the answer is true, after validating the password using password_verify, rehash it. * * @param string $hash The hash to test * @param int $algo The algorithm used for new password hashes * @param array $options The options array passed to password_hash * * @return boolean True if the password needs to be rehashed. */ function password_needs_rehash($hash, $algo, array $options = array()) { $info = password_get_info($hash); if ($info[''algo''] != $algo) { return true; } switch ($algo) { case PASSWORD_BCRYPT : $cost = isset($options[''cost'']) ? $options[''cost''] : 10; if ($cost != $info[''options''][''cost'']) { return true; } break; } return false; } /** * Verify a password against a hash using a timing attack resistant approach * * @param string $password The password to verify * @param string $hash The hash to verify against * * @return boolean If the password matches the hash */ public function password_verify($password, $hash) { if (!function_exists(''crypt'')) { trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING); return false; } $ret = crypt($password, $hash); if (!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) { return false; } $status = 0; for ($i = 0; $i < strlen($ret); $i++) { $status |= (ord($ret[$i]) ^ ord($hash[$i])); } return $status === 0; } }

Recientemente he estado tratando de implementar mi propia seguridad en un script de inicio de sesión con el que me topé en Internet. Después de luchar por tratar de aprender cómo hacer mi propio script para generar una sal para cada usuario, me topé con password_hash.

Por lo que entiendo (basado en la lectura de esta página: http://php.net/manual/en/faq.passwords.php ), la sal ya se genera en la fila cuando usas password_hash. ¿Es esto cierto?

Otra pregunta que tuve fue, ¿no sería inteligente tener 2 sales? ¿Uno directamente en el archivo y otro en el DB? De esa manera, si alguien compromete tu sal en la base de datos, ¿todavía tienes la que está directamente en el archivo? Leí aquí que almacenar sales nunca es una idea inteligente, pero siempre me confundió lo que la gente quería decir con eso.


Existe una clara falta de discusión sobre la compatibilidad con versiones anteriores y posteriores que está integrada en las funciones de contraseña de PHP. Notablemente:

  1. Compatibilidad con versiones anteriores: las funciones de contraseña son esencialmente un contenedor bien escrito alrededor de crypt() , y son inherentemente compatibles con hashes de formato crypt() , incluso si utilizan algoritmos hash obsoletos y / o inseguros.
  2. Compatibilidad con reenvíos: la inserción de password_needs_rehash() y un poco de lógica en su flujo de trabajo de autenticación puede mantenerlo actualizado con algoritmos actuales y futuros con cero cambios futuros en el flujo de trabajo. Nota: Cualquier cadena que no coincida con el algoritmo especificado se marcará por necesitar una repetición, incluidos los hashes no compatibles con criptas.

P.ej:

class FakeDB { public function __call($name, $args) { printf("%s::%s(%s)/n", __CLASS__, $name, json_encode($args)); return $this; } } class MyAuth { protected $dbh; protected $fakeUsers = [ // old crypt-md5 format 1 => [''password'' => ''$1$AVbfJOzY$oIHHCHlD76Aw1xmjfTpm5.''], // old salted md5 format 2 => [''password'' => ''3858f62230ac3c915f300c664312c63f'', ''salt'' => ''bar''], // current bcrypt format 3 => [''password'' => ''$2y$10$3eUn9Rnf04DR.aj8R3WbHuBO9EdoceH9uKf6vMiD7tz766rMNOyTO''] ]; public function __construct($dbh) { $this->dbh = $dbh; } protected function getuser($id) { // just pretend these are coming from the DB return $this->fakeUsers[$id]; } public function authUser($id, $password) { $userInfo = $this->getUser($id); // Do you have old, turbo-legacy, non-crypt hashes? if( strpos( $userInfo[''password''], ''$'' ) !== 0 ) { printf("%s::legacy_hash/n", __METHOD__); $res = $userInfo[''password''] === md5($password . $userInfo[''salt'']); } else { printf("%s::password_verify/n", __METHOD__); $res = password_verify($password, $userInfo[''password'']); } // once we''ve passed validation we can check if the hash needs updating. if( $res && password_needs_rehash($userInfo[''password''], PASSWORD_DEFAULT) ) { printf("%s::rehash/n", __METHOD__); $stmt = $this->dbh->prepare(''UPDATE users SET pass = ? WHERE user_id = ?''); $stmt->execute([password_hash($password, PASSWORD_DEFAULT), $id]); } return $res; } } $auth = new MyAuth(new FakeDB()); for( $i=1; $i<=3; $i++) { var_dump($auth->authuser($i, ''foo'')); echo PHP_EOL; }

Salida:

MyAuth::authUser::password_verify MyAuth::authUser::rehash FakeDB::prepare(["UPDATE users SET pass = ? WHERE user_id = ?"]) FakeDB::execute([["$2y$10$zNjPwqQX//RxjHiwkeUEzwOpkucNw49yN4jjiRY70viZpAx5x69kv.",1]]) bool(true) MyAuth::authUser::legacy_hash MyAuth::authUser::rehash FakeDB::prepare(["UPDATE users SET pass = ? WHERE user_id = ?"]) FakeDB::execute([["$2y$10$VRTu4pgIkGUvilTDRTXYeOQSEYqe2GjsPoWvDUeYdV2x////StjZYHu",2]]) bool(true) MyAuth::authUser::password_verify bool(true)

Como nota final, dado que solo puede volver a hacer hash la contraseña de un usuario al iniciar sesión, debería considerar "eliminar" hashes heredados inseguros para proteger a sus usuarios. Con esto quiero decir que después de un cierto período de gracia, eliminas todos los hashes inseguros [por ejemplo, MD5 / SHA / de otro modo débiles] y haces que tus usuarios confíen en los mecanismos de restablecimiento de contraseña de tu aplicación.


He creado una función que uso todo el tiempo para la validación de contraseñas y para crear contraseñas, por ejemplo, para almacenarlas en una base de datos MySQL. Utiliza una sal generada aleatoriamente que es mucho más segura que usar una sal estática.

function secure_password($user_pwd, $multi) { /* secure_password ( string $user_pwd, boolean/string $multi ) *** Description: This function verifies a password against a (database-) stored password''s hash or returns $hash for a given password if $multi is set to either true or false *** Examples: // To check a password against its hash if(secure_password($user_password, $row[''user_password''])) { login_function(); } // To create a password-hash $my_password = ''uber_sEcUrE_pass''; $hash = secure_password($my_password, true); echo $hash; */ // Set options for encryption and build unique random hash $crypt_options = [''cost'' => 11, ''salt'' => mcrypt_create_iv(22, MCRYPT_DEV_URANDOM)]; $hash = password_hash($user_pwd, PASSWORD_BCRYPT, $crypt_options); // If $multi is not boolean check password and return validation state true/false if($multi!==true && $multi!==false) { if (password_verify($user_pwd, $table_pwd = $multi)) { return true; // valid password } else { return false; // invalid password } // If $multi is boolean return $hash } else return $hash; }


Nunca use md5 () para proteger su contraseña, incluso con sal, ¡siempre es peligroso!

Asegure su contraseña con los últimos algoritmos de hash como se muestra a continuación.

<?php // Your original Password $password = ''121@121''; //PASSWORD_BCRYPT or PASSWORD_DEFAULT use any in the 2nd parameter /* PASSWORD_BCRYPT always results 60 characters long string. PASSWORD_DEFAULT capacity is beyond 60 characters */ $password_encrypted = password_hash($password, PASSWORD_BCRYPT); ?>

Para hacer coincidir la contraseña encriptada de la base de datos y la contraseña ingresada por el usuario, use la siguiente función.

<?php if (password_verify($password_inputted_by_user, $password_encrypted)) { // Success! echo ''Password Matches''; }else { // Invalid credentials echo ''Password Mismatch''; } ?>

Si desea usar su propia sal, use su función generada personalizada para lo mismo, solo siga a continuación, pero no lo recomiendo, ya que se encuentra en desuso en las últimas versiones de PHP.

lea este http://php.net/manual/en/function.password-hash.php antes de usar el siguiente código.

<?php $options = [ ''salt'' => your_custom_function_for_salt(), //write your own code to generate a suitable & secured salt ''cost'' => 12 // the default cost is 10 ]; $hash = password_hash($your_password, PASSWORD_DEFAULT, $options); ?>

Espero que todo esto ayude !!


Sí, es verdad. ¿Por qué dudas de las preguntas frecuentes de php sobre la función? :)

El resultado de ejecutar password_hash() tiene cuatro partes:

  1. el algoritmo usado
  2. parámetros
  3. sal
  4. hash de contraseña real

Como puede ver, el hash es parte de él.

Claro, podría tener una sal adicional para una capa adicional de seguridad, pero honestamente creo que es excesivo en una aplicación php normal. El algoritmo predeterminado de bcrypt es bueno, y el opcional blowfish es posiblemente incluso mejor.


Sí, lo entendió correctamente, la función password_hash () generará una sal por sí misma y la incluirá en el valor hash resultante. Almacenar la sal en la base de datos es absolutamente correcto, hace su trabajo incluso si se conoce.

// Hash a new password for storing in the database. // The function automatically generates a cryptographically safe salt. $hashToStoreInDb = password_hash($_POST[''password''], PASSWORD_DEFAULT); // Check if the hash of the entered login password, matches the stored hash. // The salt and the cost factor will be extracted from $existingHashFromDb. $isPasswordCorrect = password_verify($_POST[''password''], $existingHashFromDb);

La segunda sal que mencionaste (la que está almacenada en un archivo) es en realidad una pimienta o una clave del lado del servidor. Si lo agrega antes del picadillo (como la sal), entonces agrega un pimiento. Sin embargo, hay una mejor manera, primero puede calcular el hash y luego cifrar (dos vías) el hash con una clave del lado del servidor. Esto le brinda la posibilidad de cambiar la clave cuando sea necesario.

A diferencia de la sal, esta clave debe mantenerse en secreto. La gente a menudo lo mezcla y trata de ocultar la sal, pero es mejor dejar que la sal haga su trabajo y agregar el secreto con una llave.


Usar password_hash es la forma recomendada de almacenar contraseñas. No los separe a DB y archivos.

Digamos que tenemos la siguiente entrada:

$password = $_POST[''password''];

No valido la entrada solo por entender el concepto.

Primero hash la contraseña haciendo esto:

$hashed_password = password_hash($password, PASSWORD_DEFAULT);

Luego vea la salida:

var_dump($hashed_password);

Como puede ver, es hash. (Asumo que hiciste esos pasos).

Ahora almacena este hash_password en su base de datos, y luego digamos cuando un usuario solicita iniciar sesión. Verifica la entrada de la contraseña con este valor hash en la base de datos, haciendo esto:

// Query the database for username and password // ... if(password_verify($password, $hashed_password)) { // If the password inputs matched the hashed password in the database // Do something, you know... log them in. } // Else, Redirect them back to the login page.

Referencia oficial