Lezione Precedente Elenco Lezioni Lezione Successiva

Laboratorio di Sistemi Informativi

Autenticazione

In una applicazione Internet è plausibile che l'accesso alle funzioni di interrogazione sia libero per tutti. Nel nostro caso, chiunque deve poter interrogare la base di dati per sapere quali sono gli oggetti nella mancolista. Tuttavia, le funzioni di modifica della base di dati devono essere protette, in modo tale che soltanto le persone autorizzate possano utilizzarle. Occorre quindi prevedere un sistema per l'autenticazione degli utenti. Anche se il server SQL dispone di un meccanismo di controllo dei diritti di accesso, di solito non è conveniente utilizzare direttamente questo sistema. I diritti a livello di server SQL vegono concessi dall'amministratore del database alle diverse figure che su di esso operano (il progettista, lo sviluppatore delle applicazioni, il gruppo di testing). Di solito, ad una applicazione viene associato un unico utente (o anche di più, ma comunque in numero limitato) per effettuare le connessioni al DBMS. Se l'applicazione, al suo interno, ha bisogno di distinguere tra diversi utilizzatori, è meglio che sia lei stessa ad autenticarli, in modo da non congestionare il sistema di gestione utenti del DBMS.


ATTENZIONE! Prima di proseguire, si noti che da ora in poi, per eseguire i programmi PHP di esempio, avremo bisogno della funzione di utilità redirect_browser (vedi esercizio 2 della lezione sull'inserimento a 3 fasi). La sua definizione, migliorata rispetto alla soluzione dell'esercizio, si trova nel file funzioni.php.


Primi passi per una sistema di autenticazione

In prima approssimazione, possiamo pensare di autenticare l'utente direttamente nelle pagine che implementano le funzionalità che si intende proteggere. Ad esempio, supponendo di voler limitare l'accesso alla pagina di inserimento dei nuovi oggetti, possiamo utilizzare l'implementazione presentata in inserimento-pwd.php. In questo file, l'elenco degli utenti autorizzati e le rispettive password sono memorizzate nell'array $utenti, che si trova definito alla riga 8. La differenza fondamentale rispetto alla procedura di inserimento vista nelle lezioni scorse è che, nella parte relativa all'inserimento vero e proprio, prima di procedere si controlla se la username e password immessi corrispondono a quelle presenti nella tabella $utenti.

Questa soluzione, benché funzionante, non è molto flessibile. Ogni qualvolta occorre modificare i dati di autenticazione (ad esempio per aggiungere un nuovo utente, o cambiare la password di uno già esistente) bisogna mettere mano al codice PHP. Una soluzione molto più comoda si ottiene memorizzando nomi utenti e password all'interno del database MySQL. Attenzione che questo non equivale a gestire l'autenticazione tramite MySQL: sarà sempre l'applicazione a controllare che l'utente inserisca le credenziali di autenticazioni corrette, operando sui propri dati. L'unica differenza rispetto alla soluzione precedente è che questi ultimi sono memorizzati sul database invece di essere codificati all'interno del codice PHP.

A questo scopo, utilizziamo la tabella utenti del database mancolista, che ricordiamo avere la seguente struttura:

mysql> describe utenti;
+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| id       | int(11)     | NO   | PRI | NULL    | auto_increment | 
| username | varchar(16) | NO   | UNI | NULL    |                | 
| password | char(40)    | NO   |     | NULL    |                | 
| admin    | tinyint(1)  | NO   |     | 0       |                | 
+----------+-------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

Questa tabella contiene sia gli utenti ordinari del sito, che possono prenotare un oggetto, sia gli utenti amministratori, che possono modificari la mancolista. Il campo admin server a distinguere tra di essi. Sono già presenti dei dati in questa tabella, ma per ora lasciamoli stare, e inseriamo due nuovi amministratori con i comandi:

INSERT INTO utenti VALUES (null,'admin1','pass1',true);
INSERT INTO utenti VALUES (null,'admin2','pass2',true);

Il file inserimento-pwd.php può essere modificato in modo che utenti e password vengano controllati a partire dai dati presenti nel database, eliminando la variabile $utenti. Otteniamo allora il file inserimento-pwd-db.php.

Questa soluzione è già molto migliore di quella di prima, perché adesso per modificare le credenziali di autenticazione è sufficiente modificare il database. Tuttavia, non è di solito una buona idea memorizzare delle password in chiaro, come invece abbiamo fatto noi. Infatti, in questo modo, chiuque abbia accesso in lettura alla tabella utenti è immediatamente a conoscenza di tutte le password di accesso al sistema. Inoltre, a meno di utilizzare delle comunicazioni cifrate (che non sono argomento di questo corso), potrebbe essere possibile per un malintenzionato ascoltare la comunicazione tra server web e server MySQL e venire a conoscenza delle password degli utenti mano a mano che si collegano.

Decidiamo allora di non memorizzare nella tabella utenti la password in chiaro, ma una sua versione offuscata. Utilizziamo allo scopo la funziona intrinseca di MySQL SHA1. Impropriamente si potrebbe dire che SHA1 prende una stringa in input e ne restituisce una versione cifrata. In realtà questo non è vero. La funzione SHA1 non cifra la stringa in input, ma ne calcola una firma: una valore binario di 160 bit che è rappresentato come stringa di 40 caratteri esadecimali. La differenza sta nel fatto che, quando si parla di cifratura, si suppone che sia possibile, dal dato cifrato, risalire al dato originario, conoscendo una qualche chiave di decodifica. Nel caso della funzione SHA1 questo non è possibile, perchè stringhe diverse possono dare origine alla stessa firma.

Possiamo dare allora i seguenti comandi a MySQL:

UPDATE utenti SET password=SHA1('pass1') WHERE username='admin1';
UPDATE utenti SET password=SHA1('pass2') WHERE username='admin2';

Modifichiamo adesso il file inserimento-pwd.php in modo da utilizzare la firma della password invece della password originale. Per far ciò, si noti che anche PHP dispone di una funzione sha1 che calcola esattamente la stessa cosa di quella di MySQL. Per controllare se le credenziali inserite sono corrette, si calcola prima di tutto la firma della password immessa dall'utente e poi si controlla se, nella tabella utenti, è presente una riga che ha la stessa username immessa dall'utente e la stessa firma della password. Otteniamo il file inserimento-pwd-dbhashed.php. La differenza col programma di prima è minima. Si è semplicemente rimpiazzata la riga

$stmt->execute(array($_POST['user'],$_POST['passwd']));
con
$stmt->execute(array($_POST['user'],sha1($_POST['passwd'])));
In alternativa, si può far eseguire l'operazione di calcolo della firma direttamente a MySQL, lasciando l'invocazione a execute() come era prima, ma modificando la query SQL in questo modo:
$stmt = $conn->prepare("SELECT * FROM utenti WHERE username=? and password=SHA1(?) and admin=true");
$stmt->execute(array($_POST['user'],$_POST['passwd']));
Il vantaggio della prima soluzione è che la password non viene inviata in chiaro da PHP al MySQL, cosa che, come abbiamo detto prima, potrebbe costituire un problema per la sicurezza del sistema.

Sessioni e autenticazione

Ovviamente, questo tipo di interfaccia alle funzioni di autenticazione è molto scomoda. Ogni volta che inseriamo un nuovo volo bisogna fornire username e password. Sarebbe molto più comodo avere una pagina di login iniziale per autenticare l'utente una volta per tutte. La corretta autenticazione da il diritto di accedere alle funzioni riservate di modifica della base di dati. Questo è possibile con l'uso delle sessioni.

La prima cosa da fare è realizzare una pagina per il login all'area protetta: login.php. Quando lo script viene richiamato senza parametri, visualizza una form per la richiesta di username e password. Premendo il pulsante "Login" lo script viene eseguito con i parametri user e password che assumono i valori forniti dall'utente. A questo punto lo script controlla sulla tabella utenti se le informazioni sono valide: se sì imposta la variabile di sessione admin al valore true, altrimenti chiede di riprovare.

La funzione di inserimento è adesso realizzata da inserimento-session.php. Il codice è molto simile a quello di inserimento-pwd.php, con la differenza che all'inizio del file si controlla se l'utente è autorizzato ad accedere alle funzioni di inserimento, consultando se la variabile $_SESSION['admin'] è definita oppure no. Notare che, in teoria, dovremmo controllare se $_SESSION['admin'] è uguale true oppure no, ma per come è scritto il programma, $_SESSION['admin'] esiste allora è uguale true, per cui ci basta usare le funzione isset. Ovviamente questo non è detto che sia vero in generale.

La pagina logout.php utilizza la funzione session_destroy() per cancellare la sessione corrente. Da quel punto in poi, l'utente deve rieffettuare il login per essere ammesso alle funzioni privilegiate.

Ovviamente, chiamare manualmente le pagine di login e logout è scomodo. È possibile integrare tutte queste funzionalità modificando opportunamente la pagina con l'elenco degli oggetti, ottenendo elenco3.php. La nuova pagina, oltre a prevedere opportuni link alle pagine di Login e Logout, visualizza o nasconde i link per la modifica, inserimento e cancellazione oggetti a seconda che l'utente sia autenticato come amministratore oppure no. Ovviamente servono anche le nuove versioni delle altre pagine: login3.php, logout3.php e inserimento3-session.php.


ATTENZIONE!. Notare che il fatto che da elenco3.php si possa raggiungere le procedure di modifica e cancellazione solo se l'utente è autenticato come amministratore, non impedisce a qualcuno di accedere a quelle pagine semplicemente scrivendo la URL nella barra degli indirizzi. Occorre quindi modificare tutte le pagine della procedure di modifica e cancellazione in modo da controllare se l'utente è autenticato o meno, come fatto per la procedura di inserimento.

Esercizi e Soluzioni

Esercizio 1

Modificare gli script login3.php e elenco3.php in modo che il nome dell'utente loggato (anche se non è amministratore) compaia nella pagina dell'elenco. I link alle operazioni di cancellazione, modifica e inserimento devono essere visualizzati, come è adesso, solo se l'utente loggato è amministratore.

Lezione Precedente Elenco Lezioni Lezione Successiva

Valid HTML 4.01 Transitional Valid CSS!