Una delle novità più interessanti della versione 5.5 di PHP è l’introduzione di un’API (Application Programming Interface) per il password hashing; nel corso di questa breve trattazione verrà descritta la sintassi necessaria per utilizzare questa interfaccia per la programmazione e proposti alcuni esempi pratici riguardanti il suo impiego.
Hash e password hashing
Tecnicamente è possibile affermare che un hash rappresenta una sorta di impronta digitale, detta appunto fingerprint, associata ad un dato; in informatica è previsto un processo matematico ad “una via”, quindi per definizione irreversibile, al fine di creare un hash a lunghezza fissa a partire da una qualsiasi stringa di testo.
L’obiettivo delle operazioni di hashing è quindi quello di rendere teoricamente impossibile, e in pratica particolarmente difficoltoso, il recupero della stringa originale utilizzata per la generazione dell’hash; gli attuali algoritmi crittografici rendono poi estremamente improbabili i fenomeni denominati collisioni hash, questi ultimi riguardano sostanzialmente la possibilità che due input differenti producono il medesimo output generando ambiguità.
L’hashing delle password, utilizzato per i sistemi di autenticazione, viene introdotto perché in caso di intrusioni a carico dei database non sia possibile visualizzare le password “in chiaro”.
Nel caso specifico di PHP, l’API per il Password Hashing è stata introdotta per dotare la funzione crypt()
di un wrapper, cioè di un modulo concepito per semplificarne l’utilizzo; tale funzione, ha il compito di restituire stringhe sottoposte a cifratura attraverso un algoritmo basato su DES (Data Encryption Standard) nei sistemi Unix o Unix like o il corrispondente fornito dal sistema utilizzato.
La nuova API è stata integrata direttamente nel core del linguaggio, quindi per il suo impiego non si avrà la necessità di eseguire alcuna operazione di installazione, della configurazione di direttive specifiche nel file php.ini o del ricorso a librerie esterne.
Le costanti predefinite per il password hashing
Sostanzialmente, l’interfaccia di programmazione per il password hashing fornita da PHP a partire dalla release 5.5, mette a disposizione soltanto due costanti predefinite denominate rispettivamente PASSWORD_BCRYPT
e PASSWORD_DEFAULT
.
La prima costante potrà essere utilizzata per generare password hash tramite l’algoritmo CRYPT_BLOWFISH
. Quest’ultimo è un’implementazione basata su Blowfish, una soluzione dedicata alla cifratura a blocchi (block cipher) grazie alla quale sarà possibile criptare un insieme di elementi simultaneamente; la sua azione si basa su uno schema di crittografica simmetrica per il quale, data una chiave di cifratura, si potrà calcolare una corrispondente chiave per la decriptazione.
PASSWORD_DEFAULT
è invece una costante riferita all’algoritmo predefinito da utilizzare nel caso in cui non ne venga richiesto uno specifico.
Le funzionalità di PHP per il password hashing
PHP 5.5 associa alcune funzioni native alla nuova API per la cifratura delle password; la prima a cui fare riferimento è in questo caso password_hash
(), grazie ad essa infatti sarà possibile generare un nuovo password hash.
La sintassi di base di questa funzione prevede che ad essa vengano passati due parametri, il primo argomento è quello relativo alla password da cifrare, il secondo, denominato “algo”, viene introdotto per specificare quale algoritmo dovrà essere utilizzato per l’esecuzione dell’hash.
Nel caso in cui vengano passati soltanto i due parametri citati e lo schema di riferimento per la criptazione sia bcrypt
di Blowfish, si potranno utilizzare due opzioni addizionali, “salt” e “cost”, contenute all’interno di un array associativo.
Il salt è una sequenza casuale di bit introdotta con lo scopo di rendere più complessi i tentativi di violazione; in pratica, grazie all’adozione di un salt, il tempo impiegato per attaccare una password criptata non ridurrà il tempo necessario per tentare di crackare un’altra password. Se non dovesse essere specificato alcun salt, esso verrà prodotto per auto-generazione, diversamente quello definito andrà a sostituire quello auto-generato.
Il cost è un’opzione concepita per controllare il parametro relativo al tempo impiegato dalla CPU per la restituzione dell’hash. Un impegno prolungato da parte della CPU dovrebbe incrementare il livello di resistenza agli attacchi, mentre un’esecuzione più breve dovrebbe invece pesare di meno a carico delle risorse disponibili da parte del server.
Per chiarire quanto esposto fino ad ora sarà possibile formulare un esempio relativo all’impiego di password_hash() con e senza opzioni salt e cost:
<?php /* generazione di un password hash attraverso l'API di PHP 5.5 */ // creazione di un hash tramite l'algoritmo di cifratura predefinito echo password_hash("una_password_qualsiasi", PASSWORD_DEFAULT)."n"; // opzioni per la cifratura basata su bcrypt $options = [ 'cost' => 5, 'salt' => 'Sdfkb5m679iRTnMMluvb8D', ]; // creazione di un hash tramite bcrypt echo password_hash("una_password_qualsiasi", PASSWORD_BCRYPT, $options)."n"; ?>
L’’esecuzione del codice proposto potrebbe dar luogo ad un risultato sul modello del seguente output:
$2y$10$jg9aH0bLTkw94n0qwti4budbyh2nQqrQcvGZIkssFW72ooa1Lfm5u $2y$05$Sdfkb5m679iRTnMMluvb8.1jAiTteh1DB2.Jyq4sJRvMxOv2K9dJq
Tale esito è dovuto ad un sorgente suddivisibile in due parti distinte. Nella prima, la funzione password_hash() raccoglie come parametri la stringa da cifrare e la costante relativa all’algoritmo predefinito (PASSWORD_DEFAULT); nella seconda, invece, viene definito innanzitutto un array associativo contenente le chiavi cost e salt a cui sono stati associati i relativi valori, tale vettore viene poi passato come parametro alla funzione insieme alla stringa da cifrare e alla costante PASSWORD_BCRYPT che specifica l’algoritmo da utilizzare.
Si noti la presenza in entrambi gli hash risultanti del prefisso $2y$, esso in pratica specifica quale variante dell’algoritmo è stata utilizzata; nel 2011 venne infatti scoperta una vulnerabilità a carico di un’implementazione di terze parti di bcrypt, gli sviluppatori decisero quindi di introdurre un nuovo prefisso con il quale indicare in modo disambiguo che l’hash generato sia stato effettivamente il prodotto di una variante successiva alla scoperta della problematica citata.
Un’altra prova del fatto che in entrambe le esecuzioni sia stato utilizzato lo schema bcrypt, indipendentemente dalla costante passata come parametro, sta nel fatto che tutti e due gli output siano costituiti da una stringa di 60 caratteri.
In linea generale, si consiglia di non definire un salt personalizzato nell’impiego di password_hash() se questo non dovesse essere strettamente necessario per lo sviluppo del proprio progetto, ma di lasciare questo compito al sistema in modo da sfruttare i vantaggi derivanti da una sua produzione casuale tramite elaborazione.
Verifica della validità di un hash e rehashing
Le operazioni di verifica sulla validità di un hash possono essere eseguite grazie all’utilizzo della funzione password_verify(); essa accetta come parametri una password “in chiaro” e un hash, come nell’esempio seguente:
<?php
/*
verifica della validità di un hash
*/
$hash = '$2y$05$Sdfkb5m679iRTnMMluvb8.1jAiTteh1DB2.Jyq4sJRvMxOv2K9dJq';
if (password_verify('una_password_qualsiasi', $hash)) {
echo 'Password valida.';
} else {
echo 'Password non valida.';
}
?>
In pratica, password_verify()
controlla che vi sia una corrispondenza tra una password e un hash, in caso di esito positivo restituirà TRUE, altrimenti il suo risultato sarà FALSE.
L’API per il Password Hashing di PHP prevede che sia possibile effettuare una procedura di rehashing dopo la modifica delle opzioni utilizzate per la generazione dell’hash originale, come per esempio l’alterazione del cost; a questo scopo è disponibile l’apposita funzione password_needs_rehash(), un semplice esempio riguardante il suo utilizzo potrebbe essere strutturato in questo modo:
]<?php
/*
verifica di un hash e password rehash
*/
function password_rehash($password, $hash) {
// verifica della corrispondenza tra stringa e hash
if (!password_verify($password, $hash)) {
// valore di ritorno in caso si mancata corrispondenza
return false;
}
// se l'hash necessità di essere rigenerato..
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
// ..ciò sarà possibile utilizzando l'algoritmo predefinito
$hash = password_hash($password, PASSWORD_DEFAULT);
// azione conseguente al rehashing
}
return true;
}
?>
La funzione proposta è stata scritta per la risoluzione di un caso particolare, quello relativo al rehashing successivo alla modifica dell’algoritmo predefinito. Essa in pratica controlla tramite password_verify() che vi sia una corrispondenza tra la stringa per la cifratura e l’hash generato, in seconda istanza verifica se l’hash necessita di rehashing in quanto ottenuto tramite un algoritmo diverso da quello corrente; la produzione di un nuovo hash avverrà quindi soltanto nel caso sia necessaria, lo sviluppatore potrà poi implementare un’azione conseguente al rehashing come, ad esempio, l’aggiornamento di un record in un database per la memorizzazione del nuovo hash.
Conclusioni
Con la nuova API per il Password Hashing, PHP 5.5 fornisce un modulo per l’utilizzo semplificato della funzione crypt() e per la generazione di hash più sicuri rispetto a quelli basati su md5 e sha1; nel corso di questa trattazione sono state descritte le caratteristiche, la sintassi e le funzioni di tale interfaccia nonché proposti alcuni esempi del suo utilizzo pratico.