Salted Hash in PHP

By robob on 2008-12-11-22:06:55 | In php hash

Mi è venuto in mente che un po' di tempo fa ho buttato giù qualche riga di codice in PHP per implementare un meccanismo di memorizzazione e verifica di password utente con il meccanismo del salted hash.

Il codice che vedrete compare anche nel blog del mio amico Roberto Scaccia, quindi non vi preoccupate non c'è nessun copia e incolla, ma solo una condivisione di codice con il caro amico Roberto (Link).

Questa è la classe in PHP che modella la logica (potete estenderla anche ad altri algoritmi oltre lo SHA1):


/**
* Classe per la manipolazioni degli hash di sicurezza
*
*/
class Hasher 
{
 private $text;
 private $nchar_salt;
 private $nchar_cycle;
 
 // Costruttore
 // @text testo di cui calcolare l'hash
 // @ncahr_salt numero di caratteri da utilizzare per il salt
 // @nchar_cycle numero dei caratteri necessari per memorizzare i cicli 
 function __construct($text, $nchar_salt, $nchar_cycle)
 {
  $this->text = $text;
  $this->nchar_salt = $nchar_salt;
  $this->nchar_cycle = $nchar_cycle;
 }
 
 // Calcola un salt randomico come concatenazione di caratteri scelti
 // randomicamente nell'intervallo di cahrcode (32,127)
 static private function get_salt($nchar_salt) 
 {
  $s = "";
  for ($i=0;$i<$nchar_salt;$i++) 
   $s .= chr(rand(32,127));   
  return $s;
 }
 
 // Calcola lo Sha1 + salt per un certo numero di cicli
 public function secure_sha1($cycle = 1000) 
 {
  $salt = Hasher::get_salt($this->nchar_salt);
  return $this->secure_sha1_2($cycle, $salt);
 }

 // Calcola lo Sha1 per un certo numero di cicli
 public function secure_sha1_2($cycle = 1000, $salt) 
 {  
  $sha1 = $this->text;
  for ($i=0;$i<$cycle;$i++)
   $sha1 = sha1($sha1.$salt, TRUE);  
    
  $cycle = str_pad($cycle,$this->nchar_cycle,"0",STR_PAD_LEFT);
  $sha1 = $sha1.$salt.$cycle;  
  return base64_encode($sha1);
 }
 
 // Verifica che il testo corrisponda all'hash
 public function verify_sha1($b64_text)
 {
  // Faccio la decodifica Base64  
  $text = base64_decode($b64_text);
 
  $n = strlen($text);
  // Ricavo il numero di caratteri del salt
  $cycle = substr($text,$n-$this->nchar_cycle, $this->nchar_cycle);
  
  $n -= $this->nchar_cycle;
  //ricavo il SALT
  $salt = substr($text,$n-$this->nchar_salt, $this->nchar_salt);
    
  // Ricalcolo l'hash e controllo se coincide   
  $calculated_hash = $this->secure_sha1_2($cycle, $salt); 
  
  if ($calculated_hash==$b64_text) return true;
  return false;  
 }
}

Per generare il salted hash della propria password:

$hash = new Hasher("thisismypassword", 8,8);
$salted_hash = $hash->secure_sha1(1000);

Per verificare che la password immessa dall'utente corrisponda a quella memorizzata come salted hash nel file delle password (in questo caso un file XML):

// Autentica l'utente
static public function authenticate($us, $pd, $filename) {
  
 // leggo l'XML dal file
 $xmlDOM=Logic::read_xml($filename);
  
 // Verifico che la password immessa corrisponda a quella hashata nel file
 $hash = new Hasher($pd, 8,8);
 $ret = $hash->verify_sha1($xmlDOM->hashedpassword[0]);
  
 if ($us==$xmlDOM->username[0] && $ret==true) {
  return true;
 } 
 return false; 
}

Non sono stato prodigo di spiegazione off-code, ma i commenti ci sono e sono anche abbastanza esplicativi. Ciao MisterX