Per la cronaca, questo era puramente a scopo di apprendimento, mi è stato detto di non usarlo nemmeno per un sito web personale che non mi aspetto che qualcuno possa visitare, quindi questo post è, purtroppo, l'ultimo che vedrò di questo codice. Tuttavia, ho fatto tutte le cose che ho letto e mi sembra soddisfacente (ma non sono un esperto), quindi sono solo curioso di sapere quali parti potrebbero essere abbastanza brutte da consigliare a tutti di non fare mai mai usare una soluzione personalizzata?
Non nominare nessuno, ma sono abbastanza sicuro di aver già fatto un miglio lavoro migliore del webhost gratuito che inizia con un haha triplo numero, la mia password in chiaro è stata rubata da lì.
Ecco l'idea di base di ciò che ho fatto:
Salt e IV
Ogni sale è generato da una funzione casuale ma non crittograficamente sicura in PHP (è comunque meglio del caso casuale predefinito). Viene quindi crittografato con blowfish utilizzando una chiave definita nel codice e una IV definita in un file effettivo (protetto da htaccess). Il valore crittografato è memorizzato, quindi è molto semplice cambiare la chiave IV o salt per invalidare tutte le password. Inoltre, se il database è compromesso, i sali non possono essere decifrati senza accedere al file system.
Hash
Utilizza lo stretching chiave e sha512. La quantità di allungamento della chiave è definita da un modulo di un valore crc32 della password e sale, quindi ogni utente ha una quantità diversa e è possibile modificare il valore per consentire tempi di elaborazione più lunghi. Ogni hash di ripetizione usa alcuni hash al suo interno nel tentativo di mantenerlo abbastanza casuale.
Ecco il codice reale che ho, se non conosci PHP è ancora abbastanza facile vedere cosa sta succedendo una volta per volta:
$salt_key = 'íSgüý[Îaí²zÿCV™F÷œDDa©^fU©êm‹“.Ù ®Kâ³ÃX}rÿ,¢\ˆÿ7L³¢QÆH’YeTsÙõŸ¥éÙí#W—c´VÆ';
function get_iv(){
// It stores it as a file, but there's no point showing that part so here's the actual code bit
return mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC), MCRYPT_RAND);
}
function generate_salt(){
global $salt_key;
$salt = substr(uniqid(mt_rand(), true), 0, 32);
$key = hash('sha256', $salt_key, true);
$iv = get_iv();
$encrypt = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $salt, MCRYPT_MODE_CFB, $iv);
return array($salt, $encrypt);
}
function decrypt_salt($encrypted){
global $salt_key;
$key = hash('sha256', $salt_key, true);
$iv = get_iv();
return mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $encrypted, MCRYPT_MODE_CFB, $iv);
}
function hash_password($password, $salt){
#return hash('sha512', $password.$salt, true);
$hash = $salt;
for($i=0; $i<(crc32($salt.$password) % 129197); $i++){
$hash = hash('sha512', $hash.$password.$salt.hash('sha512', $salt.$hash.$password).md5($salt.md5($password.$hash)), true);
}
return $hash;
}
La parte per verificare se la password è valida in pratica controlla semplicemente il nuovo hash rispetto a quello vecchio, quindi l'ho lasciato fuori. L'unico svantaggio basato sulle cose che ho letto che un buon algoritmo dovrebbe fare, è che non usa molta memoria.