È buona norma aumentare il livello di sicurezza delle nostre applicazioni evitando di memorizzare le password in chiaro nel database. Una buona pratica consiste nel memorizzare, invece, un hash da cui si può verificare che la password inserita dall'utente sia corretta.
MD5 e SHA1
Sino a qualche anno fa le strategie di hashing più diffuse utilizzavano la codifica in MD5 o in SHA1. Ad esempio:
$password = 'LaM1aPassW0rd';
$hash = md5($password); //b33a0446525f0e11e91bd268dbbdfdc3
$hash = sha1($password); //814486c99c198b99d7d3e8a585d8b6be324b7177
Il vantaggio di tali soluzioni è che sono "one-way", cioè non esiste un algoritmo capace di decodificarli. Tale strategia non si rivela però molto efficace dato che online esistono moltissimi tool che attraverso delle rainbow table sono in grado di restituire la stringa iniziale partendo dall'hash.
Strategie alternative consistevano nel combinare tra di loro o applicare più volte tali algoritmi alla stringa. Ad esempio:
$password = 'LaM1aPassW0rd';
$hash = md5(md5($password);
$hash = md5(sha1($password);
Un'altra strategia, applicata anche insieme a quelle già viste, consiste nel combinare un salt alla password, cioè una stringa di caratteri casuali che dovrebbe aumentare la complessità.
$password = 'LaM1aPassW0rd';
$salt = '1dh3(3@2';
$hash = md5($password . $salt);
Tutte queste tecniche di codifica continuano ad avere un problema alla base: l'algoritmo di codifica è veloce, questo significa che un malintenzionato può eseguire miliardi di tentativi al secondo su una singola CPU. In termini più semplici, per quanto ci possiamo impegnare a rendere complesso l'hash, resta comunque probabile che si riesca a risalire alla password originale in tempi non molto elevati.
La funzione crypt()
Un'alternativa molto più sicura in PHP consiste nell'utilizzare la funzione crypt()
.
La funzione prende in ingresso due parametri:
$str
: la stringa da criptare.$salt
: il salt da applicare. Questo campo è opzionale ma altamente consigliato per evitare di generare password deboli.
Tale funzione utilizza l'algoritmo standard DES di Unix oppure, in base al tipo di salt, uno tra quelli disponibili nel sistema tra:
Algoritmo | Descrizione |
---|---|
CRYPTSTDDES | Standard basato su DES con chiave di due caratteri. |
CRYPTEXTDES | Estensione basata su DES con chiave di nove caratteri. |
CRYPT_MD5 | Crittografia basata su MD5 con 12 caratteri di chiave inizianti con $1$ . |
CRYPT_BLOWFISH | Crittografia Blowfish con 16 caratteri di chiave inizianti con $2$ o $2a$ . |
CRYPT_SHA256 | Crittografia basata su SHA-256 con 16 caratteri di chiave inizianti con $5$ . |
CRYPT_SHA512 | Crittografia basata su SHA-512 con 16 caratteri di chiave inizianti con $6$ . |
$password = 'LaM1aPassW0rd';
echo 'Standard DES: ' . crypt($password, 'rl') . "\n";
echo 'Extended DES: ' . crypt($password, '_J9..rasm') . "\n";
echo 'MD5: ' . crypt($password, '$1$rasmusle$') . "\n";
echo 'Blowfish: ' . crypt($password, '$2a$07$usesomesillystringforsalt$') . "\n";
echo 'SHA-256: ' . crypt($password, '$5$rounds=5000$usesomesillystringforsalt$') . "\n";
echo 'SHA-512: ' . crypt($password, '$6$rounds=5000$usesomesillystringforsalt$') . "\n";
Gli output che otterremo sono simili ai seguenti:
Standard DES: rlrilO6oNxQzw
Extended DES: _J9..rasmvAavwwHGzfY
MD5: $1$rasmusle$YumKbTCImTJsTsgYeybAW0
Blowfish: $2a$07$usesomesillystringfore0frKquhsqiIddYDH.rpL9GGLE4ZN1lu
SHA-256: $5$rounds=5000$usesomesillystri$xoDuebqNnfT3MOdczgqISdPxZ2C91n550ToQbY7LeP5
SHA-512: $6$rounds=5000$usesomesillystri$ih83lc2hDlh5FrDrhPdbnhzGTWI79lquzan21cOOOgRGpX9e/R21pK6HNU1JYLtqh7Xr2na51P/Ibpj2AQ1TW0
Una volta che la password è stata codificata e memorizzata, per assicurarci che l'utente stia tentando l'accesso con la password corretta abbiamo bisogno di una seconda funzione: hash_equals()
. Essa prende in ingresso due parametri:
- l'hash della password attesa.
- l'hash della password immessa dall'utente.
hash_equals()
ci restituirà un booleano in base al risultato della verifica. Vediamo un esempio:
$password = 'LaM1aPassW0rd'; //password valida
$hashedPassword = crypt($password, '$2a$07$usesomesillystringforsalt$'); //hash memorizzato nel database
$userPassword = 'LaM1aPassW0rd'; //password inserita dall'utente nel login
$hashedUserPassword = crypt($userPassword, '$2a$07$usesomesillystringforsalt$');
if(hash_equals($hashedPassword, $hashedUserPassword)) {
echo "Accesso effettuato con successo";
} else {
echo "La password inserita non è corretta";
}
Sicuramente la funzione crypt
ci consente di avere degli hash molto più sicuri rispetto agli esempi iniziali. Di contro però risulta piuttosto macchinosa da utilizzare e molto incline a errori di programmazione.