Una libreria per la gestione del Login in php
Una di quelle cose che capita a tutti di scrivere è la gestione del login/registrazione/protezione pagine per un sito.
Tempo fa avevo scritto una lib per farlo, ma non riesco a ritrovarla, e siccome era comunque scritta in php4 o peggio ho deciso di riscriverla da zero
In internet non sono riuscito a trovare nulla che vada aldilà di snippet mal documentati, quindi penso possa essere utile a qualcuno oltre che a me.
Userò php5 e Adodb5 per la gestione del database. cominciamo
La struttura del database e la configurazione di adodb
Adodb
cominciamo con adodb, per installarlo si dovrà semplicemente scaricarne l’ultima versione e scompattarla su una cartella e poi includerne il file con gli header ogni volta che ci serve. per ottenerne un istanza includeremo un file che contiene i parametri di inizializzazione e una funzione per inizializzarne un istanza. il file, che chiamaremo “dbConfig.php” è il seguente:
<?php include_once('../lib/adodb5/adodb.inc.php'); $_DBCONFIG = array ( 'host' => "localhost", 'db_port' => "3306", 'db_user' => "darshan", 'db_pass' => "darshan", 'db_name' => "database1" ); function get_db(){ global $_DBCONFIG; $DB = NewADOConnection('mysql'); $DB->Connect($_DBCONFIG['host'], $_DBCONFIG['db_user'], $_DBCONFIG['db_pass'], $_DBCONFIG['db_name']); return $DB; } ?>
La tabella sul database
la tabella che useremo sarà la seguente:
CREATE TABLE `users` ( `userid` int(25) NOT NULL AUTO_INCREMENT, `email_address` varchar(100) NOT NULL, `username` varchar(25) NOT NULL DEFAULT '', `password` varchar(255) NOT NULL DEFAULT '', `user_level` enum('user','admin') NOT NULL DEFAULT 'user', `signup_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `last_login` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `activated` enum('0','1') NOT NULL DEFAULT '0', `salt` char(3) DEFAULT NULL, PRIMARY KEY (`userid`) ) ENGINE=MyISAM AUTO_INCREMENT=25 DEFAULT CHARSET=UTF8
Il significato dei campi è evidente, tranne forse il salt di cui ci occuperemo dopo
Registrazione
vediamo adesso come registrare un utente, partiremo da una form con 4 textbox che saranno i parametri della nostra funzione:
function createSalt() { $string = md5(uniqid(rand(), true)); return substr($string, 0, 3); } function do_register($username, $pass1,$pass2, $mail){ if(!checkEmail($mail)) return array(false, "la mail non è valida ". $mail); if($pass1 != $pass2) return array(false, "le due password non coincidono"); if(strlen($username) > 25) return array(false, "l'user name è troppo lungo"); $hash = hash('sha256', $pass1); $salt = createSalt(); $hash = hash('sha256', $salt . $hash); $DB=get_db(); $username = mysql_real_escape_string($username); $ok = $DB->Execute("SELECT * FROM users WHERE username=? ",$username); if ( $ok === false) return array(false, $DB->ErrorMsg()); $c= $ok->RecordCount(); if($c>0) return array(false, "l'utente esiste"); $query = "INSERT INTO users ( username, password, salt , email_address, signup_date) VALUES ( '$username' , '$hash' , '$salt' , '$mail', NOW() );"; $ok = $DB->Execute($query); if ( $ok === false) return array(false, $DB->ErrorMsg()); return array(true, "Registrazione effettuata, l'account è in attesa di attivazione"); }
la funzione ha come valore di ritorno un vettore di 2 elementi, il primo è un booleano che ci dice se la funzione ha avuto successo e il secondo è il messaggio di output, vediamo come funziona:
- per prima cosa verifica che la mail sia valida ( l’utilissima checkEmail($email) è presa da qui
- poi verifica che le due pass inserite coincidano
- poi che il nome utente non sia troppo lungo
- dopo crea l’hash, aggiunge un salt e ripete l’hash…
salt? è che diavolo è un salt?
sappiamo tutti che le funzioni di hashing sono difficilmente invertibili, ma questo non ci salva da un attacco dizionario, infatti se l’utente mette una pass banale il suo hash potrebbe essere contenuto in un database di hash come questo http://www.hash-database.net/ il salt è una stringa casuale che viene anteposta hall hash della password creando una stringa lunga e complessa che verà poi hashata una seconda volta, in fase di login si effettuerà la procedura inversa. in questo modo anche se il database venisse violato o se finisse in mano a un sysadmin carogna sarebbe comunque quasi impossibile effettuare l’inversione delle password.
- inizializza il db ed esegue l’escape del utente ( eh si, mysql_real_escape_string necessita di una connessione attiva per sapere con quale charset deve lavorare)
- poi verifica che non ci sia già un utente con quel nome ( la funzione RecordCount() di ADO conta le righe ritornate dalla query)
- e infine scrive l’utente sul database, notare che l’utente non risulta attivato e ha come grado di default ‘user’
il resto delle funzioni lo descriverò nel prossimo articolo, anche perché fisicamente non le ho ancora scritte nemmeno in php.