Salted Hash con PHP
Dopo il post di Daniele Frongia e il loro paper sugli Hash in PHP (che vi consiglio di leggere) mi è venuto in mente che un po' di tempo fa avevo 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.
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;
function __construct($text, $nchar_salt, $nchar_cycle)
{
$this->text = $text;
$this->nchar_salt = $nchar_salt;
$this->nchar_cycle = $nchar_cycle;
}
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 + salt 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;
}
Se il vostro provider non vi permette di mettere il file XML al di fuori della web root del sito allora un trucchetto è quello di rinominare il file XML con l'estensione PHP. Il server Web tenterà di interpretare il PHP, qualora qualcuno tenti di scaricare il file delle password in locale per effettuare attacchi sulle vostre password hashate (che dovrebbero comunque garantirvi un alto livello di sicurezza), non riuscendovi.
Chi volesse contribuire con commenti al codice è benvenuto.
ciao roberto, il tuo post è davvero interessante, io e raffaello certamente lo vedremo con attenzione. potrebbe essere un altro tassello al nostro lavoro una volta che sara' su wikibooks...grazie
RispondiElimina