|
Oggi ho pensato di presentarvi un progetto che ho sviluppato qualche tempo addietro, per :
1) imparare il PHP (bellissimo !)
2) realizzare un framework per creare siti internet secondo il pattern MVC.
3) imparare come è stato implementato l'object-orientation in php5.
Come dire se vuoi fare qualcosa ... falla in grande!
Il framework implementa sia la tecnologia XML sia Ajax oltre ad avere un possibile back-end MySQL, dico possibile perchè in realtà per realizzare una pagina web, come quelle dei famosi CMS PhpNUKE, Joomla, Midgard ecc., basta inserire un file e non appoggiarsi per forza ad un server MySQL, risparmiando sia risorse di sistema sia economiche.
Passiamo ora al solo sistema di autenticazione, visto che è una delle parti progettualmente più complesse ma operativamente più semplici, il resto lo presenterò in un prossimo futuro.
Resta importante specificare che il Login Manager ha la peculiarità di effettuare la fusione e criptazione delle credenziali di accesso, inserite dall'utente, direttamente lato client !!!. Esso, infatti, realizza l'hashing md5 dello username, della password e di una stringa generata casualmente dal sistema e visualizzata direttamente a video in formato immagine (sistema CAPTCHA) inviandole poi al server il quale, ricevendo lo username, conoscendo la stringa casuale (da lui generata) ed avendo ricevuto l'md5 della username-password-stringa casuale, provvede alle successive operazioni.
Per ottimizzare l'autenticazione ho provveduto a realizzare l'invio delle credenziali in una stringa composta da :
username = username inserito + login = credenziali criptate
Es.: pippo+123jhfo34xd~5ttTTyhzalmncttsk4585@qq0pP-w (vi sfido ad indovinare :-) )
in questo modo il sistema effettuerà prima una ricerca dell'utente (pippo) che, se presente, permetterà la continuazione delle operazioni, in caso contrario sarà visualizzato un messaggio di diniego.
Peculiarità di questo sistema di login:
1) Trasmissione sicura delle credenziali d'accesso (criptate)
2) Invio asincrono, il sistema in effetti implementa una logica di autenticazione a chiave pubblica/chiave privata, ove la chiave pubblica è lo username e password e la chiave privata è di fatto la stringa casuale generata ed inviata al client sotto forma d'immagine. In questo modo un malintenzionato che intercetta il nostro traffico sin dall'inizio, per scoprire username e password, non è in grado di identificarle inquanto:
- nonostante sia in possesso della stringa casuale non conoscerebbe username e password che viaggiano, al ritorno, in forma criptata.
- se dovesse conoscere username e password non potrebbe usare la stringa casuale intercettata, perchè è già stata "bruciata" dall'utente durante il login.
3) Sicuro, se l'utente non esiste non si procede ad ulteriori operazioni fra client e server.
4) Unicità delle credenziali d'accesso, l'utente usa le sue ma il sistema riceve un'insieme diverso ogni volta, come in un sistema O.T.P.
5) Elaborazione lato client completamente invisibile ad un malintenzionato che accede fisicamente alla macchina della vittima. La criptazione viene effettuata con un applicativo compilato e residente sul client, quindi non direttamente interpretabile (applicazione Flex su pagina html).
6) Possibilità di applicare algoritmi proprietari di criptazione per aumentarne la segretezza (basta conoscere Javascript o mxml).
7) Delega della segretezza delle credenziali all'utente ed al gestore del sistema informatico (il sistema di autenticazione è sicuro, tutto il resto non è nostro compito ;-) ).
8) Redirezionando l'utente, dopo il login-success, ad una comunicazione con protocollo https, si raggiunge una completa segretezza della trasmissione ed una sicura identificazione dell'utente sin dalla fase di autenticazione.
9) Web oriented, "gira" su tutte le piattaforme PHP-MySQL lato server e con un lettore file swf (noto come file Flash) lato client.
10) Uso di tecnologie già note e notevolmente diffuse.
... ah dimenticavo tutto Open Source !!!
Un esempio? li trovate qui
| Administrator |
paperino |
password |
Power User
|
pippo |
drowssap |
| User |
topolino |
password |
| Guest |
paperone |
onecent |
Ricordando che il sistema è configurato per riconoscere 4 tipi di livelli di autenticazione :
| 1 |
Administrator |
Administrator of site |
| 2 |
PowerUser |
Power user of the site |
| 3 |
User |
User of the site |
| 4 |
Guest |
Guest of the site |
L'esito dell'autenticazione visualizzerà una pagina che ne riporta tutte le caratteristiche.
Passiamo ad una piccola analisi del codice :
FlexLogin.mxml
In questo file abbiamo il codice utile per creare l'interfaccia di login, al suo interno troveremo le seguenti funzioni :
Funzione per eseguire il login:
public function makeLogin():String { return (MD5.encrypt(username.text + password.text + txtCaptcha.text)); }
Funzione per inviare i dati di autenticazione
public function sendInfo():void { var url:String = "http://localhost/pxaserver/libreria/loginresult.php"; var info:String = "username="+ username.text+"&login="+makeLogin(); var request:URLRequest = new URLRequest(url); var login:URLVariables = new URLVariables(info); request.method="POST"; request.data = login; navigateToURL(request); }
Funzione per effettuare la registrazione di un nuovo utente
public function register_user():void{ var url:String = "http://localhost/pxaserver/secure_area/registeruser.php"; var goto:URLRequest = new URLRequest(url); navigateToURL(goto); }
Funzione ricorda utente
public function remember_password():void{ var url:String = "http://localhost/pxaserver/secure_area/remember.php"; var goto:URLRequest = new URLRequest(url); navigateToURL(goto); }
Da notare che in ogni funzione la variabile url:String è stata inizializata ad un URL, ricordatevi che quest'ultimo deve essere un URL del vostro server. Es.: http://www.miodominio.it/libreria/loginresult.php
All'interno della funzione makeLogin (che restituisce un risultato di tipo stringa) noterete la chiamata ad un metodo statico "MD5.encrypt". Questa chiamata ci permette di effetture l'hash con MD5, della stringa passata, questa è la stringa che verrà inviata insieme con lo username.
Preciso che non è meta di questo articolo quello di spiegare la compilazione di un file mxml con gli strumenti open-source, presenti in rete, fate comunque riferimento al sito di www.adobe.com, troverete tutti gli spunti del caso.
Passiamo ora al codice lato server.
Abbiamo detto che è stato usato il linguaggio PHP che nella sua versione 5, permette di usare un'orientamento ad oggetti.
Innanzi tutto osserviamo i file denominato index.php che deve essere posizionato all'interno della directory che conterrà in seguito tutti i file che vorremo proteggere dal sistema dio login. Notate come la prima istruzione si aun require ... e la seconda (quindi la prima eseguita) sia una chiamata alla funzione login_session_control(). Questa funzione deve essere inserita in ogni file che vogliamo proteggere.
Index.php
<?php // inizio controllo sessione area privata require ('../libreria/libfunz.inc.php'); login_session_control(); // fine controllo sessione per area privata
$sessi = new Session; print("Il risultato del form di login :<br><br>"); print("Il valore di username : ". $_POST["username"]."<br>"); print("Il valore di password : ". $_POST["password"]."<br>"); print("Il valore di login passato : ". $_POST["login"]."<br>"); echo "<br>ID di sessione = " . $sessi -> get_session_id(); echo "<br>La stringa captcha e' : ".$sessi -> get_session_value("captcha"); echo "<br>La stringa login e' : ".$sessi -> get_session_value("login"); echo "<br><br>".perform_login($_POST["username"],$_POST["password"],$_POST["login"]); ?>
Ecco di seguito un estratto del file Libfunz ed in particolare le due funzioni presentate.
Libfunz.inc.php
Funzione per eseguire il login
function perform_login ($username,$password, $login){ /* * Funzione per effettuare il login * $username: username dell'utente * $password: password dell'utente * $login: stringa criptata in MD5 del login che contiene * username + password + captcha */ $aut = new Autentica; $aut -> login($username,$password,$login); return $aut -> login_result(); }
Funzione per controllare la sessione. Questa funzione viene inserita all'interno della prima pagina che deve essere visualizzatadopo il login (quella nell'area privata), in questo modo nel caso non si sia già provveduto ad effettuarte il login, l'accesso verrà negato e si sarà redirezionati all'apposito form delle credenziali.
function login_session_control(){ // da inserire prima di tutto!!!! /* * Funzione per controllare se è stato effettuato il login */ //if(session_id() == "") { session_start(); } require ('config.inc.php'); $s = new Session; $s -> init_session(); if (($s -> get_session_value("login")== null)|| ($s -> get_session_value("login")!="success")){ header("Location:".$login_page); //print("<script>window.location='".$login_page."'</script>"); }else { $s -> regenerate_session(); } }
All'interno della funzione perform_login() si può notare l'istanza della classe Autentica. Quest'ultima serve a gestire in toto il processo di login.
Classe Autentica
<? /* Classe per l'autenticazione con chiave privata Guerrieri Luca
This e-mail address is being protected from spambots. You need JavaScript enabled to view it
*/
class Autentica { //Classe per l'autenticazione private $username; private $password; private $level; //livello utente private $check_username_result; //bool risultato username dell'autenticazione private $check_password_result; //bool risultato password dell'autenticazione private $link; //il link alla connessione del database private $login_array; //array risualtato dal login private $md5_string; //stringa di login codificata md5 private $s; //puntatore ad una istanza della classe Sessione private $log; //puntatore ad una istanza della classe LOGManager private $msg_error; //puntatore ad una istanza della classe MSGmanager function Autentica (){ //costruttore include_once("global.inc.php"); $this -> msg_error= new MSGmanager; //Istanzia la classe dei messaggi di errore $this -> s = new Session; //Istanzia la classe per estire le sessioni $this -> s -> init_session(); //inizializza la sessione $this -> log = new LOGmanager; } //Metodo per effettuare il login public function login($username, $login, $ip) { //prende in carico il login ed inizia l'autenticazione //non abbiamo bisogno di controllare la correttezza della password //perchè fa parte della stringa di login include ("config.inc.php"); //Richiede il file di configurazione generale $this->check_username($username); //chiama il metodo check_username per controllare se esiste lo $username $ora= (string)date("d-m-Y H:i:s"); if (($this->check_username_result)== true){ //username buono $md5_login= $this->username.$this->password.$this -> s -> get_session_value("captcha"); $this->md5_string=md5($md5_login); if (($this->md5_string)==$login){ //le stringhe di login sono uguali $this -> level = $this->login_array[0]['level']; $this -> set_check_username_result(true); $this -> s -> set_session_var("login", "success"); $this -> s -> set_session_var("time", $ora); $this -> s -> set_session_var("level", $this->level); //memorizziamo la sessione nel db $Qm_s = new QueryManager; $Qm_s -> set_query('Q_set_session'); $Qm_s -> set_placeholders(array("user_id","session_id","login_time")); $Qm_s -> set_data(array($this->login_array[0]['username'],$this -> s -> get_session_id(),$ora)); $Qr_s = new QueryResult($Qm_s->get_dynamic_query(),"direct",$this->link); $logmsg = array ($this->msg_error->print_msg('auth_log_operation_success'),$this->login_array[0]['username'],"session id : ".$this -> s -> get_session_id(),$ip,time()); $this -> log ->write_csv_row_log_file($auth_log, $logmsg); }else{ //le stringhe di login non sono uguali $this -> set_check_username_result(false); $this -> s -> set_session_var("login", "required"); $logmsg=array ($this->msg_error->print_msg('auth_log_operation_fails'),$this->login_array[0]['username'],$this->msg_error->print_msg('auth_log_operation_fails_string'),$ip,time()); $this -> log ->write_csv_row_log_file($auth_log, $logmsg); } } else { //username no buono $this-> set_check_username_result(false); $this -> s -> set_session_var("login", "required"); $logmsg = array ($this->msg_error->print_msg('auth_log_operation_fails'),$username,$this->msg_error->print_msg('auth_log_operation_fails_user'),$ip,time()); $this -> log ->write_csv_row_log_file($auth_log, $logmsg); } }
//Metodo che controlla l'esistenza dell'utente public function check_username($username) { //si connette al db e controlla se l'utente esiste altrimenti rilascia un messaggio di errore $this->username=$username; $Qm = new QueryManager; $Qm ->set_query('Q_username'); $Qm ->set_placeholders(array('username')); //imposto i nomi dei placeholders nella query come array $Qm ->set_data(array($this->username)); //imposto i nomi delle variabili nella query come array $Qr = new QueryResult($Qm ->get_dynamic_query(),"assoc",$this->link); //rilascia un array con il risultato della query eseguita $this-> login_array=$Qr->result; //print_r($this->login_array); //per controllare il risultato if ((!is_array($this-> login_array))||(is_null($this-> login_array))){ print ($this->msg_error->print_error('auth_login_query_error')); }else { if (count ($this->login_array)>0){ $this->set_check_username_result(true); //username buono }else { $this->set_check_username_result(false); //username no buono } } $this -> password=$this->login_array[0]['password']; //estrae la password } //Metodo che controlla se la password inserita è giusta private function check_password($password){ //Inserire il controllo della password criptata if ($this->password == $password){ //la password è giusta return $this->set_check_password_result(true); }else { //la password non è giusta return $this->set_check_password_result(false); } } //Metodo che rilascia il risultato dell'autenticazione public function get_check_username_result(){ return $this->check_username_result; } //Metodo che imposta il risultato dell'autenticazione private function set_check_username_result($result){ $this->check_username_result=$result; } //Metodo che imposta il risultato dell'autenticazione private function set_check_password_result($result){ $this->check_password_result=$result; } //Metodo che imposta il link al db public function set_db_link($link){ $this->link=$link; } //Metodo che rilascia il risultato dell'autenticazione public function get_check_password_result(){ return $this->check_password_result; } //Metodo che restituisce la password dell'autenticazione public function get_user_password(){ return $this->password; } //Metodo per inserire una sessione nel db tabella sessioni private function set_session_id(){ } //Metodo che esegue le operazioni in seguito all'esito del tentativo di login public function login_result (){ //controlliamo il risultato return $this->get_check_username_result(); } } ?>
All'interno della funzione login_session_control() possiamo invece notare la classe Session Quest'ultima gestisce, istanzia e chiude una sessione oltre che rinnovarla per motivi di sicurezza.
Classe Session
<?php class Session {
function Session() { //costruttore della classe (overload) require('config.inc.php'); //Richiede il file di configurazione generale $this -> error= new MSGmanager; //istanzia la classe per i messaggi //if ($init==true){$this -> init_session();} //inizializza la sessione }
//Metodo per cancellare un Session ID public function del_session() { $_SESSION=array(); if (IsSet ($_COOKIE[session_name()])){ setcookie(session_name(), '', time()-42000, '/'); } session_destroy(); }
//Metodo per cancellare un cookie public function unset_cookie($cookie_name,$cookie_value){ if(IsSet($_COOKIE[$cookie_name])){ setcookie($cookie_name,$cookie_value,time()-31536000); } }
//Metodo per impostare una sessione public function init_session() { session_start(); //inizia o continua la sessione //$_SESSION[$session_var] = $session_value; //$this -> set_cookie($session_var,$session_value); }
//Metodo per inizializzare una variabile di Sessione public function set_session_var($session_var,$session_value) { $_SESSION[$session_var] = $session_value; }
//Metodo per resettare una variabile registrata in sessione public function unset_session_var($session_var) { unset ($_SESSION[$session_var]); }
//Metodo per rigenerare una sessione public function regenerate_session(){ return session_regenerate_id(); }
//Metodo per impostare i cookie public function set_cookie($cookie_name,$cookie_value){ $this -> cookie_enable(); setcookie($cookie_name,$cookie_value,time()+31536000); }
//Metodo per sapere se i cookie sono abilitati public function cookie_enable ($disabled_cookie_url=""){ if(!IsSet($_COOKIE['PHPSESSID'])){ //AGGIUNGERE MESSAGGIO DEL MSG MANAGER print ("Per poter usufruire di questi servizi si devono avere i cookie abilitati"); return $cookie_enable=false; }else{ return $cookie_enable=true; } } //Metodo per leggere i cookie public function get_cookie_value($var) { if (IsSet($_COOKIE[$var])){ return $_COOKIE[$var]; }else { //print ("Attenzione non c'e' la variabie cercata nel cookie"); //se la variabile nel cookie non è presente ritorna false return false; }
} //Metodo per leggere una sessione public function get_session_value($var) { if (IsSet($_SESSION[$var])){ return $_SESSION[$var]; }else { //print ("Attenzione non c'e' la variabie cercata nella sessione"); //se la variabile non è presente ritorna false return false; }
} //Metodo per leggere il SessionID da un cookie di sessione public function get_cookie_session_id() { if (IsSet($_COOKIE['PHPSESSID'])){ return $_COOKIE['PHPSESSID']; }else { print ("Attenzione non è stata impostata una sessione valida"); }
} //Metodo per leggere il SessionID public function get_session_id() { return session_id(); } } ?>
Infine, quando effettuiamo l'invio dei dati, viene invocato, per mezzo di query string, il file loginresult.php. Se tutto il processo di login è andato a buon fine permetterà la visualizzazione delle variabili inerenti l'autenticazione.
Loginresult.php
<?php include_once ('global.inc.php'); require_once ("config.inc.php"); require_once ("libfunz.inc.php");
if (perform_login($_POST["username"],$_POST["login"])==true){ //se il login ha successo redirezioniamo all'area privata $s = new Session; $s -> init_session(); if ($s->get_session_value("level")==1){header ("Location:".$admin_area);}else{header ("Location:".$secure_area);} }else { //se il login non ha successo redirizioniamo al login page header ("Location:".$login_page); //codice per pop-up di ritorno come //utente disabilitato //utente non trovato //password o hash errati } ?>
Avrete notato come le classi presentate fanno uso di altre classi che per ora non presento perchè degne di un'analisi dettagliata, a parte.
|