Come validare una password generata da WordPress nel database usando PHP?

25 ott 2011, 01:27:04
Visualizzazioni: 18.9K
Voti: 5

Sto lavorando su un sito realizzato con WordPress e devo aggiungere alcune sezioni esterne al CMS, oltre a verificare l'accesso degli utenti al di fuori di WP.

Ho provato con l'md5 della password ma non funziona...

Ho provato questo codice:

require_once( 'wp-includes/class-phpass.php' );
$wp_hasher = new PasswordHash( 8, TRUE );
$password = "passwordqui";
$hashed_password = $wp_hasher->HashPassword( $password );
$encryptedpass = md5($hashed_password);

Ma questo serve solo per la creazione iniziale della password, e risulta sempre diverso.

Mi serve un codice che possa essere usato in questo modo:

SELECT * FROM wp_customers WHERE email = "ccc@aaa.com" AND password = "<cosa va qui?>"

È possibile farlo in qualche modo?

Grazie.

0
Tutte le risposte alla domanda 4
2

In base alla tua altra domanda... sembra che tu stia cercando di validare una password in testo semplice rispetto a quella memorizzata nel database. Ecco la funzione che WordPress utilizza per fare esattamente questo:

function wp_check_password($password, $hash, $user_id = '') {
    global $wp_hasher;

    // Se l'hash è ancora in md5...
    if ( strlen($hash) <= 32 ) {
        $check = ( $hash == md5($password) );
        if ( $check && $user_id ) {
            // Rigenera l'hash utilizzando il nuovo sistema.
            wp_set_password($password, $user_id);
            $hash = wp_hash_password($password);
        }

        return apply_filters('check_password', $check, $password, $hash, $user_id);
    }

    // Se l'hash memorizzato è più lungo di un MD5, presume che sia
    // il nuovo stile di hash portatile phpass.
    if ( empty($wp_hasher) ) {
        require_once ( ABSPATH . 'wp-includes/class-phpass.php');
        // Di default, utilizza l'hash portatile da phpass
        $wp_hasher = new PasswordHash(8, TRUE);
    }

    $check = $wp_hasher->CheckPassword($password, $hash);

    return apply_filters('check_password', $check, $password, $hash, $user_id);
}

Innanzitutto, questo plugin verifica se l'hash MD5 della password fornita è uguale alla password memorizzata (hashata) per un utente. Verifica anche se l'hash PHPass della password fornita è uguale alla password memorizzata per un utente.

Puoi seguire uno schema simile.

Quindi supponiamo che ti venga fornito un nome utente e una password dall'utente e che tu voglia validarli (my_password_validation( $username, $password )). Utilizzerai il nome utente fornito per recuperare una password hashata dal database. Poi confronti l'hash della password fornita con il valore memorizzato per vedere se è valida.

Ecco del pseudocodice non testato:

function my_password_validation( $username, $password ) {
    // Seleziona l'hash della password dell'utente dal database
    $stored = query( 'SELECT * FROM wp_customers WHERE email = ' . $username );

    require_one( 'class-phpass.php' );
    $hasher = new PasswordHash(8, TRUE);

    return $hasher->CheckPassword( $password, $stored );
}

Se la password che passi alla funzione viene hashata nello stesso modo del valore memorizzato, la funzione restituirà true. Altrimenti restituirà false.

Guardando i commenti che hai lasciato sull'altra domanda, sembra che tu abbia altri problemi, però. Per citare:

Quindi ottengo:

$P$BqVYujC/jqNY4aylZpHi475jwcaSUs1 Ma come posso confrontarlo con quello nel DB?

Quello nel DB è:

fa063a4ed35e092a2d4e15c1b6a61871 Come confrontare questi due con MySQL?

Posso dirti subito che la password che stai ottenendo dal database non è stata hashata utilizzando l'utility PHPass. Quegli hash assomiglieranno sempre a quelli che iniziano con $P$B perché è quello che indica al sistema come è stato hashato. PHPass è basato su Blowfish che utilizza quel tipo di prefisso per le stringhe cifrate.

Il tuo hash fa063... sembra più un classico hash MD5... quindi se il tuo hash MD5 del testo semplice non corrisponde, penso che potresti avere la password sbagliata.

Per rispondere alla tua domanda "come confronto questi due con MySQL"... non lo fai. MySQL è il deposito dei dati... non eseguire logiche di business o confronti nel deposito dei dati. Leggi i dati, poi utilizza uno script PHP per eseguire il tuo confronto.

25 ott 2011 06:08:41
Commenti

Questi discorsi "MySQL è solo un archivio dati... non fare alcuna logica di business o confronti nell'archivio dati" sono un po' esagerati, onestamente. I server di database sono strumenti potenti. Sicuramente non sono "solo" archivi dati e negli ultimi anni sono stati sottovalutati a scapito di tutti, secondo me.

Craig Tullis Craig Tullis
17 giu 2015 02:35:53

Nel database di WordPress, c'è una tabella degli utenti, e in quella tabella c'è un campo user_pass. È quello l'hash "memorizzato" che si invia come secondo parametro alla funzione $hasher->CheckPassword()?

Millar248 Millar248
11 apr 2021 08:34:47
3

È molto semplice..

<?php
include_once($_SERVER['DOCUMENT_ROOT'].'/wp-includes/class-phpass.php' );

// prepara la connessione al database
$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 "Connessione fallita: " . mysql_error();
            exit();
}

// username di WordPress di cui confrontare la password
$user = 'test';
$user_name = htmlspecialchars($user,ENT_QUOTES);

// password in chiaro da confrontare
$password = 'tespass';

$hasher = new PasswordHash(8, TRUE);

// ottieni la password hashata dell'utente dal database di 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];
}

// confronta la password in chiaro con quella hashata
if ($hasher->CheckPassword( $password, $passnya )){
    echo "CORRISPONDENZA TROVATA";
} else {
    echo "NESSUN RISCONTRO";
}
?>
25 set 2013 06:14:19
Commenti

PasswordHash non è una classe core di php, quindi se non la includi ottieni un errore. Ricorda che l'utente ha chiesto di validare la password al di fuori di WordPress, quindi le classi e le funzioni di WP non sono disponibili.

gmazzap gmazzap
25 set 2013 07:56:26

@ialocin è una classe core di WordPress, non una classe core di PHP.

gmazzap gmazzap
10 lug 2015 13:54:05

Colpa mia! Avrei dovuto leggere più attentamente.. @gmazzap

Nicolai Grossherr Nicolai Grossherr
10 lug 2015 13:56:19
0

Ho avuto questo problema e l'ho risolto proprio su wp_hash_password()

Confronta una password già crittografata con la sua versione in testo semplice

<?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 "SI, Corrisponde";
} else {
    echo "No, Password Errata";
}

?>
17 lug 2015 03:32:19
2

Finalmente, quasi 10 anni dopo. Ho trovato la risposta esatta a ciò che la domanda stava cercando.

Vuoi farlo utilizzando PHP Core, giusto? Ecco il codice per te! Prima devi aggiungere una classe: -> copiala semplicemente da wp-includes/class-phpass.php -> non preoccuparti. È scritto in PHP core.

class PasswordHash {
    var $itoa64;
    var $iteration_count_log2;
    var $portable_hashes;
    var $random_state;

    /**
     * Costruttore 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); // rimosso getmypid() per motivi di compatibilità
    }

    /**
     * Costruttore 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);
        # Usiamo "$P$", phpBB3 usa "$H$" per la stessa cosa
        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;

        # Siamo costretti a usare MD5 qui poiché è l'unica
        # primitiva crittografica disponibile in tutte le versioni di PHP
        # attualmente in uso. Implementare la nostra crittografia a basso livello
        # in PHP comporterebbe performance peggiori e
        # di conseguenza in conteggi di iterazione più bassi e hash che sono
        # più facili da craccare (da codice non-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);
        # Questo dovrebbe essere dispari per non rivelare chiavi DES deboli, e il
        # valore massimo valido è (2**24 - 1) che è comunque dispari.
        $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)
    {
        # Questo ha bisogno di usare un ordine diverso di caratteri e uno
        # schema di codifica diverso da quello in encode64() sopra.
        # Ci interessa perché l'ultimo carattere nella nostra stringa codificata
        # rappresenterà solo 2 bit. Mentre due implementazioni conosciute di
        # bcrypt accetteranno felicemente e correggeranno una stringa di salt che
        # ha i 4 bit non utilizzati impostati su non-zero, non vogliamo rischiare
        # e non vogliamo sprecare un byte aggiuntivo
        # di entropia.
        $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;

        # Restituire '*' in caso di errore è sicuro qui, ma non sarebbe sicuro
        # in una funzione simile a crypt(3) usata sia per generare nuovi
        # hash che per validare password contro hash esistenti.
        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;
    }
}

Hai quasi finito.

$password = 'binary1001';//password che vuoi verificare

$hasher = new PasswordHash(8, TRUE);
$passnya = '$P$BFG8I1k171qgRKqZvj0K3tn3bBSrsW/';//hash di quella password dal db di wp


// confronta la password in chiaro con quella hashata
if ($hasher->CheckPassword( $password, $passnya )){
    echo "CORRISPONDENTE";
} else {
    echo "NON CORRISPONDENTE";
}

Ci sono arrivato con tentativi ed errori. Ora è il momento per te di arrivare al punto. Buon coding!!!

28 mar 2021 18:43:44
Commenti

Ma questa è solo la risposta di BagusS con l'accesso al database rimosso, vero? E anche quella è essenzialmente la parte della risposta di EAMann contrassegnata come 'pseudocodice non testato', che è stata pubblicata dieci anni fa.

Rup Rup
29 mar 2021 00:54:36

sì! hai ragione! Volevo solo mostrare che funziona davvero e la risposta completa in una volta sola!

Md. Hasan Mahmud Md. Hasan Mahmud
30 mar 2021 05:29:24