Cómo validar una contraseña generada por WordPress en la base de datos usando PHP
Estoy trabajando con un sitio hecho en WordPress y necesito agregar algunas partes externas a WP, verificando el login de usuarios fuera de WordPress.
Intenté con md5 de la contraseña pero no funciona...
Probé este código:
require_once( 'wp-includes/class-phpass.php' );
$wp_hasher = new PasswordHash( 8, TRUE );
$password = "passwordhere";
$hashed_password = $wp_hasher->HashPassword( $password );
$encryptedpass = md5($hashed_password);
Pero esto solo sirve para crear la contraseña por primera vez, y siempre es diferente.
Necesito código que pueda usarse en esto:
SELECT * FROM wp_customers WHERE email = "ccc@aaa.com" AND password = "<¿qué va aquí?>"
¿Es posible hacer esto de alguna manera?
Gracias.
Basándome en tu otra pregunta... parece que estás intentando validar una contraseña en texto plano contra lo almacenado en la base de datos. Esta es la función que WordPress utiliza para hacer exactamente eso:
function wp_check_password($password, $hash, $user_id = '') {
global $wp_hasher;
// Si el hash sigue siendo md5...
if ( strlen($hash) <= 32 ) {
$check = ( $hash == md5($password) );
if ( $check && $user_id ) {
// Volver a hashear usando el nuevo hash.
wp_set_password($password, $user_id);
$hash = wp_hash_password($password);
}
return apply_filters('check_password', $check, $password, $hash, $user_id);
}
// Si el hash almacenado es más largo que un MD5, se asume
// el nuevo estilo de hash portable phpass.
if ( empty($wp_hasher) ) {
require_once ( ABSPATH . 'wp-includes/class-phpass.php');
// Por defecto, usa el hash portable de phpass
$wp_hasher = new PasswordHash(8, TRUE);
}
$check = $wp_hasher->CheckPassword($password, $hash);
return apply_filters('check_password', $check, $password, $hash, $user_id);
}
Primero, este plugin verifica si el hash MD5 de la contraseña dada coincide con la contraseña almacenada (hasheada) de un usuario. También verifica si el hash PHPass de la contraseña dada coincide con la contraseña almacenada del usuario.
Puedes seguir un patrón similar.
Digamos que recibes un nombre de usuario y una contraseña del usuario y quieres validarlos (my_password_validation( $username, $password )
). Usarás el nombre de usuario proporcionado para obtener la contraseña hasheada de la base de datos. Luego comparas el hash de la contraseña dada con el valor almacenado para ver si es válido.
Aquí tienes un pseudocódigo no probado:
function my_password_validation( $username, $password ) {
// Selecciona el hash de la contraseña del usuario desde la base de datos
$stored = query( 'SELECT * FROM wp_customers WHERE email = ' . $username );
require_one( 'class-phpass.php' );
$hasher = new PasswordHash(8, TRUE);
return $hasher->CheckPassword( $password, $stored );
}
Si la contraseña que pasas a la función genera un hash igual al valor almacenado, la función devolverá true. De lo contrario, devolverá false.
Mirando los comentarios que dejaste en la otra pregunta, parece que tienes otros problemas. Para citar:
Entonces obtengo:
$P$BqVYujC/jqNY4aylZpHi475jwcaSUs1
¿Pero cómo puedo comparar eso con uno en la BD?El de la BD es:
fa063a4ed35e092a2d4e15c1b6a61871
¿Cómo comparar esos dos con MySQL?
Puedo decirte ahora mismo que la contraseña que estás obteniendo de la base de datos no fue hasheada usando la utilidad PHPass. Esos hashes siempre comenzarán con algo como $P$B
porque eso le dice al sistema cómo fue hasheado. PHPass está basado en Blowfish, que usa ese tipo de prefijo en cadenas cifradas.
Tu hash fa063...
parece más un hash MD5 estándar... así que si tu hash MD5 del texto plano no coincide, entonces quizás tengas la contraseña incorrecta.
Para responder a tu pregunta de "¿cómo comparo esos dos con MySQL?"... no lo haces. MySQL es el almacén de datos, no ejecutes lógica de negocio o comparaciones en el almacén de datos. Lee los datos y luego usa un script PHP para realizar tus comparaciones.

Estos comentarios de "MySQL es el almacén de datos... no hagas lógica de negocio ni comparaciones en el almacén de datos" son un poco exagerados, sinceramente. Los servidores de bases de datos son herramientas poderosas. Definitivamente no son "solo" almacenes de datos y han sido menospreciados en los últimos años, en perjuicio de todos, en mi opinión.

Es muy sencillo..
<?php
include_once($_SERVER['DOCUMENT_ROOT'].'/wp-includes/class-phpass.php' );
// preparar la conexión a la base de datos
$ip_address="localhost";
$user_db="userdb";
$pass_db="passdb";
$conn= mysql_connect($ip_address,$user_db,$pass_db);
mysql_select_db("dbname",$conn);
if (!$conn){
echo "No se pudo conectar: " . mysql_error();
exit();
}
// nombre de usuario de WordPress cuya contraseña se va a comparar
$user = 'test';
$user_name = htmlspecialchars($user,ENT_QUOTES);
// contraseña en texto plano para comparar
$password = 'tespass';
$hasher = new PasswordHash(8, TRUE);
// obtener la contraseña cifrada del usuario de la base de datos de WordPress
$queryx = "select * from wa1gty5f_users where user_login='$user_name'";
$Resultx = mysql_query($queryx,$conn);
while($row = mysql_fetch_array($Resultx)){
$passnya = $row[user_pass];
}
// comparar la contraseña en texto plano con la contraseña cifrada
if ($hasher->CheckPassword( $password, $passnya )){
echo "COINCIDEN";
} else {
echo "NO COINCIDEN";
}
?>

PasswordHash
no es una clase principal de PHP, por lo que si no la incluyes, obtendrás un error. Recuerda que el usuario pidió validar la contraseña fuera de WordPress, por lo que las clases y funciones de WP no están disponibles.

@ialocin es una clase principal de WordPress, no una clase principal de PHP.

Tuve este problema y lo descubrí justo en wp_hash_password()
Comparar una contraseña ya hasheada con su cadena de texto plano
<?php
$wp_hasher = new PasswordHash(8, TRUE);
$password_hashed = '$P$B55D6LjfHDkINU5wF.v2BuuzO0/XPk/';
$plain_password = 'test';
if($wp_hasher->CheckPassword($plain_password, $password_hashed)) {
echo "SÍ, coinciden";
} else {
echo "No, contraseña incorrecta";
}
?>

Finalmente, casi 10 años después. Encontré la respuesta exacta que la pregunta estaba buscando.
¿Quieres hacerlo usando PHP Core, verdad?
¡Aquí está el código para ti!
Primero necesitas agregar una clase:
-> solo cópiala de wp-includes/class-phpass.php
-> no te preocupes. Está escrito en PHP core.
class PasswordHash {
var $itoa64;
var $iteration_count_log2;
var $portable_hashes;
var $random_state;
/**
* Constructor PHP5.
*/
function __construct( $iteration_count_log2, $portable_hashes )
{
$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
$iteration_count_log2 = 8;
$this->iteration_count_log2 = $iteration_count_log2;
$this->portable_hashes = $portable_hashes;
$this->random_state = microtime() . uniqid(rand(), TRUE); // se eliminó getmypid() por razones de compatibilidad
}
/**
* Constructor PHP4.
*/
public function PasswordHash( $iteration_count_log2, $portable_hashes ) {
self::__construct( $iteration_count_log2, $portable_hashes );
}
function get_random_bytes($count)
{
$output = '';
if ( @is_readable('/dev/urandom') &&
($fh = @fopen('/dev/urandom', 'rb'))) {
$output = fread($fh, $count);
fclose($fh);
}
if (strlen($output) < $count) {
$output = '';
for ($i = 0; $i < $count; $i += 16) {
$this->random_state =
md5(microtime() . $this->random_state);
$output .=
pack('H*', md5($this->random_state));
}
$output = substr($output, 0, $count);
}
return $output;
}
function encode64($input, $count)
{
$output = '';
$i = 0;
do {
$value = ord($input[$i++]);
$output .= $this->itoa64[$value & 0x3f];
if ($i < $count)
$value |= ord($input[$i]) << 8;
$output .= $this->itoa64[($value >> 6) & 0x3f];
if ($i++ >= $count)
break;
if ($i < $count)
$value |= ord($input[$i]) << 16;
$output .= $this->itoa64[($value >> 12) & 0x3f];
if ($i++ >= $count)
break;
$output .= $this->itoa64[($value >> 18) & 0x3f];
} while ($i < $count);
return $output;
}
function gensalt_private($input)
{
$output = '$P$';
$output .= $this->itoa64[min($this->iteration_count_log2 +
((PHP_VERSION >= '5') ? 5 : 3), 30)];
$output .= $this->encode64($input, 6);
return $output;
}
function crypt_private($password, $setting)
{
$output = '*0';
if (substr($setting, 0, 2) == $output)
$output = '*1';
$id = substr($setting, 0, 3);
# Usamos "$P$", phpBB3 usa "$H$" para lo mismo
if ($id != '$P$' && $id != '$H$')
return $output;
$count_log2 = strpos($this->itoa64, $setting[3]);
if ($count_log2 < 7 || $count_log2 > 30)
return $output;
$count = 1 << $count_log2;
$salt = substr($setting, 4, 8);
if (strlen($salt) != 8)
return $output;
# Estamos obligados a usar MD5 aquí ya que es el único
# primitivo criptográfico disponible en todas las versiones de PHP
# actualmente en uso. Implementar nuestro propio cripto de bajo nivel
# en PHP resultaría en un rendimiento mucho peor y
# consecuentemente en menores iteraciones y hashes que son
# más fáciles de crackear (por código que no sea PHP).
if (PHP_VERSION >= '5') {
$hash = md5($salt . $password, TRUE);
do {
$hash = md5($hash . $password, TRUE);
} while (--$count);
} else {
$hash = pack('H*', md5($salt . $password));
do {
$hash = pack('H*', md5($hash . $password));
} while (--$count);
}
$output = substr($setting, 0, 12);
$output .= $this->encode64($hash, 16);
return $output;
}
function gensalt_extended($input)
{
$count_log2 = min($this->iteration_count_log2 + 8, 24);
# Esto debería ser impar para no revelar claves DES débiles, y el
# valor máximo válido es (2**24 - 1) que de todos modos es impar.
$count = (1 << $count_log2) - 1;
$output = '_';
$output .= $this->itoa64[$count & 0x3f];
$output .= $this->itoa64[($count >> 6) & 0x3f];
$output .= $this->itoa64[($count >> 12) & 0x3f];
$output .= $this->itoa64[($count >> 18) & 0x3f];
$output .= $this->encode64($input, 3);
return $output;
}
function gensalt_blowfish($input)
{
# Este necesita usar un orden diferente de caracteres y un
# esquema de codificación diferente al de encode64() arriba.
# Nos importa porque el último carácter en nuestra cadena codificada
# solo representará 2 bits. Mientras que dos implementaciones conocidas de
# bcrypt aceptarán y corregirán felizmente una cadena de sal que
# tenga los 4 bits no usados establecidos a valores no cero, no queremos
# arriesgarnos y tampoco queremos desperdiciar un byte adicional
# de entropía.
$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$output = '$2a$';
$output .= chr(ord('0') + $this->iteration_count_log2 / 10);
$output .= chr(ord('0') + $this->iteration_count_log2 % 10);
$output .= '$';
$i = 0;
do {
$c1 = ord($input[$i++]);
$output .= $itoa64[$c1 >> 2];
$c1 = ($c1 & 0x03) << 4;
if ($i >= 16) {
$output .= $itoa64[$c1];
break;
}
$c2 = ord($input[$i++]);
$c1 |= $c2 >> 4;
$output .= $itoa64[$c1];
$c1 = ($c2 & 0x0f) << 2;
$c2 = ord($input[$i++]);
$c1 |= $c2 >> 6;
$output .= $itoa64[$c1];
$output .= $itoa64[$c2 & 0x3f];
} while (1);
return $output;
}
function HashPassword($password)
{
if ( strlen( $password ) > 4096 ) {
return '*';
}
$random = '';
if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
$random = $this->get_random_bytes(16);
$hash =
crypt($password, $this->gensalt_blowfish($random));
if (strlen($hash) == 60)
return $hash;
}
if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
if (strlen($random) < 3)
$random = $this->get_random_bytes(3);
$hash =
crypt($password, $this->gensalt_extended($random));
if (strlen($hash) == 20)
return $hash;
}
if (strlen($random) < 6)
$random = $this->get_random_bytes(6);
$hash =
$this->crypt_private($password,
$this->gensalt_private($random));
if (strlen($hash) == 34)
return $hash;
# Retornar '*' en error es seguro aquí, pero _no_ sería seguro
# en una función tipo crypt(3) usada _tanto_ para generar nuevos
# hashes como para validar contraseñas contra hashes existentes.
return '*';
}
function CheckPassword($password, $stored_hash)
{
if ( strlen( $password ) > 4096 ) {
return false;
}
$hash = $this->crypt_private($password, $stored_hash);
if ($hash[0] == '*')
$hash = crypt($password, $stored_hash);
return $hash === $stored_hash;
}
}
Ya casi estás listo.
$password = 'binary1001';//contraseña que quieres verificar
$hasher = new PasswordHash(8, TRUE);
$passnya = '$P$BFG8I1k171qgRKqZvj0K3tn3bBSrsW/';//hash de esa contraseña desde la base de datos de wp
// comparar contraseña en texto plano con contraseña hasheada
if ($hasher->CheckPassword( $password, $passnya )){
echo "COINCIDE";
} else {
echo "NO COINCIDE";
}
Llegué aquí con prueba y error. Ahora, es tu momento de llegar al punto. ¡Feliz codificación!!!

Pero eso es solo la respuesta de BagusS con el acceso a la base de datos editado, ¿no? E incluso eso es esencialmente la parte de la respuesta de EAMann que está marcada como 'pseudocódigo no probado', que se publicó hace diez años.
