Hace algún tiempo te enseñé como autenticar un usuario existente de WordPress sin usar Wordpress, solo PHP, beneficiándote del ecosistema WP para cargar y autentificar a tus usuarios en tu propia app.
Recientemente salió la versión 6.8 de WordPress y con ello ha cambiado la función de Hashing, así que toca actualizar.
El aviso está en: https://make.wordpress.org/core/2025/02/17/wordpress-6-8-will-use-bcrypt-for-password-hashing/
Comprobar contraseña de WordPress
Lo que te voy a mostrar no usa plugins y se ejecuta fuera de WP. Es una manera sucia (pero no insegura) de comprobar las contraseñas.
Previamente teníamos este código que usaba PasswordHash.php:
<?php
function autenticarUsuario($username, $password)
{
# Obtener usuario. Si no existe, regresamos false inmediatamente
$usuario = obtenerUsuarioDeWordPress($username);
if (!$usuario) return false;
# Incluimos PasswordHash pues vamos a realizar algunas comprobaciones
include_once "PasswordHash.php";
# Esta es la contraseña almacenada en la base de datos:
$storedHash = $usuario->user_pass;
# Crear instancia de PasswordHash
$passwordHash = new PasswordHash(8, false);
$metaValue = unserialize($usuario->meta_value);
# Nota: en mi caso solo permito usuarios administradores y colaboradores; ignoro a los demás.
# Si tú quieres permitirlos, modifica el código
if (!isset($metaValue["administrator"]) && !isset($metaValue["contributor"])) {
return false;
}
if ((isset($metaValue["administrator"]) && !$metaValue["administrator"]) && (isset($metaValue["contributor"]) && !$metaValue["contributor"])) {
return false;
}
# Ahora sí comprobamos la contraseña plana ($password) con la hasheada almacenada ($storedHash)
if ($passwordHash->CheckPassword($password, $storedHash)) {
# Si llegamos aquí, el usuario y contraseña son correctos
# Lo único que hacemos es poner el rol del mismo
if ((isset($metaValue["administrator"])) && $metaValue["administrator"]) {
$usuario->role = "administrator";
} else if (isset($metaValue["contributor"]) && $metaValue["contributor"]) {
$usuario->role = "contributor";
}
# Y regresamos el usuario
return $usuario;
} else {
# Caso contrario (la contraseña no coincide) regresamos igualmente false
return false;
}
}
Esto era porque las contraseñas tenían el formato de $P$B8La1sd3sa12634as5d1sa
pero
con esta actualización tienen un formato como $wp$2y$10$uhy32178321321b21j321
que es
bcrypt con modificaciones extra.
Leyendo el comunicado de actualización me di cuenta que $wp
es solo un prefijo para que
el propio WordPress pueda identificar sus hashes, pero si lo removemos tenemos un hash
totalmente válido para bcrypt.
Eso sí: antes de comprobar la contraseña plana hay que calcular su SHA384, remover el $wp
y ahora sí enviarla a bcrypt.
Me guié de los siguientes enlaces:
- https://make.wordpress.org/core/2025/02/17/wordpress-6-8-will-use-bcrypt-for-password-hashing/
- https://developer.wordpress.org/reference/functions/wp_hash_password/
- https://developer.wordpress.org/reference/functions/wp_check_password/
Para terminar con la siguiente función:
function loginUser($username, $password)
{
$user = getUserFromWordPressDatabase($username);
if (!$user) return false;
include_once "PasswordHash.php";
$storedHash = $user->user_pass;
$passwordHash = new PasswordHash(8, false);
$metaValue = unserialize($user->meta_value);
if (!isset($metaValue["administrator"]) && !isset($metaValue["contributor"]) && !isset($metaValue["subscriber"])) {
return false;
}
if ((isset($metaValue["administrator"]) && !$metaValue["administrator"]) && (isset($metaValue["contributor"]) && !$metaValue["contributor"]) && (isset($metaValue["subscriber"]) && !$metaValue["subscriber"])) {
return false;
}
$passwordCorrecta = false;
/**
* Tenemos que comprobar si el hash es nuevo porque a WP se le ocurrió actualizar su hash unos 20 años después
* https://make.wordpress.org/core/2025/02/17/wordpress-6-8-will-use-bcrypt-for-password-hashing/
*/
# Entonces es nueva 6.8
if (str_starts_with($storedHash, '$wp$2y')) {
$password_to_hash = base64_encode(hash_hmac('sha384', trim($password), 'wp-sha384', true));
$passwordCorrecta = password_verify($password_to_hash, substr($storedHash, 3));
} else {
$passwordCorrecta = $passwordHash->CheckPassword($password, $storedHash);
}
if ($passwordCorrecta) {
if ((isset($metaValue["administrator"])) && $metaValue["administrator"]) {
$user->role = "administrator";
} else if (isset($metaValue["contributor"]) && $metaValue["contributor"]) {
$user->role = "contributor";
} else if (isset($metaValue["subscriber"]) && $metaValue["subscriber"]) {
$user->role = "subscriber";
}
return $user;
} else {
return false;
}
}