PHP

password_hash en PHP: Cómo Hashear Contraseñas con bcrypt

Autorangel cruz
Publicado
Lectura3 min de lectura
Actualizado
password_hash en PHP: Cómo Hashear Contraseñas con bcrypt

password_hash() es la función nativa de PHP para hashear contraseñas de forma segura. Genera el hash usando bcrypt (o Argon2) con un salt aleatorio incluido dentro del propio hash, y luego se comprueba con password_verify(). Es la forma recomendada desde PHP 5.5: nunca uses md5() ni sha1() para contraseñas.

El uso mínimo y seguro es este:

// Al registrar: guardas el hash, nunca la contraseña en texto plano.
$hash = password_hash($password, PASSWORD_DEFAULT);
 
// Al iniciar sesión: comparas la contraseña contra el hash guardado.
if (password_verify($password, $hash)) {
    // La contraseña es correcta.
}

Con eso ya tienes un login seguro. El resto del artículo explica qué pasa por dentro y cómo afinarlo.

Por qué no usar md5() ni sha1()

Es común ver código viejo que hashea contraseñas con md5:

md5('password'); // 5f4dcc3b5aa765d61d8327deb882cf99

El problema es que md5 y sha1 son rápidos y sin salt: una GPU prueba miles de millones de combinaciones por segundo, y existen tablas (rainbow tables) que revierten esos hashes al instante. Para contraseñas necesitas justo lo contrario: un algoritmo lento y con salt, que es lo que te da password_hash().

La firma de password_hash()

Según la documentación oficial, la función recibe la contraseña, el algoritmo y un array opcional de opciones:

password_hash(string $password, string|int|null $algo, array $options = []): string

Algoritmos disponibles

  • PASSWORD_DEFAULT: el algoritmo recomendado por PHP en cada versión. Hoy es bcrypt, pero puede cambiar en el futuro, así que el hash resultante puede crecer en longitud. Guarda la columna como VARCHAR(255).
  • PASSWORD_BCRYPT: fuerza bcrypt. Genera hashes de 60 caracteres que empiezan por $2y$.
  • PASSWORD_ARGON2ID: Argon2id, disponible si PHP se compiló con soporte para Argon2. Es una buena alternativa moderna a bcrypt.

El parámetro cost

El cost controla cuánto trabajo cuesta calcular el hash: a mayor cost, más lento (y más resistente a fuerza bruta). En bcrypt el valor por defecto es 10, y subió a 12 en PHP 8.4.

$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);

Súbelo solo hasta donde tu servidor pueda calcular un hash en una fracción de segundo razonable. No definas el salt a mano: la opción salt se marcó como obsoleta en PHP 7.0 y se eliminó en PHP 8.0. password_hash() ya genera un salt criptográficamente seguro por ti.

Verificar y rehashear

Para comprobar una contraseña usas password_verify(), que vuelve a calcular el hash internamente y lo compara sin filtrar información de tiempo:

if (password_verify($password, $hashGuardado)) {
    // Acceso concedido.
}

Como el algoritmo recomendado y el cost cambian con las versiones de PHP, conviene rehashear las contraseñas cuando el usuario inicia sesión, usando password_needs_rehash():

if (password_verify($password, $hashGuardado)) {
    if (password_needs_rehash($hashGuardado, PASSWORD_DEFAULT)) {
        $nuevoHash = password_hash($password, PASSWORD_DEFAULT);
        // Guarda $nuevoHash en la base de datos.
    }
}

Así tus hashes se mantienen al día sin pedirle al usuario que cambie su contraseña.

Resumen

  • Usa password_hash($password, PASSWORD_DEFAULT) para hashear y password_verify() para comprobar.
  • Nunca uses md5 ni sha1 para contraseñas, ni guardes la contraseña en texto plano.
  • No generes el salt manualmente: ya viene incluido en el hash.
  • Guarda el hash en una columna VARCHAR(255) y rehashea con password_needs_rehash() cuando haga falta.

Lecturas recomendadas: documentación oficial de password_hash.