Nel precedente articolo avevamo realizzato un'area riservata ricorrendo a mod_auth_mysql, facendo cioè interagire Apache con MySQL. La soluzione presentata risultava maggiormente flessibile e scalabile rispetto a quella classica offerta da mod_auth, che si appoggia ad un file di testo per gestire utenti e password.
Ci eravamo proposti l'obbiettivo di proteggere una directory del nostro sito mediante un file .htaccess, memorizzando le credenziali d'accesso in una tabella del database MySQL. Dopo le operazioni preliminari d'installazione e configurazione eravamo giunti ad un primo semplice esempio:
AuthName "Area Riservata" AuthType Basic #direttive specifiche di mod_auth_mysql AuthMYSQLEnable on AuthMySQLDB apache_auth AuthMySQLUser apache_user AuthMySQLPassword miosegreto AuthMySQLPwEncryption none #fine direttive specifiche require valid-user
La tabella di MySQL creata aveva la seguente struttura:
mysql> describe user_info; +---------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------------+-------------+------+-----+---------+-------+ | user_name | varchar(30) | NO | PRI | NULL | | | user_password | varchar(20) | NO | | NULL | | +---------------+-------------+------+-----+---------+-------+
Di seguito vedremo come sia possibile agire sulle direttive del file di configurazione per-directory, per ottenere soluzioni più avanzate.
La cifratura delle password
Il primo elemento da migliorare è, senza ombra di dubbio, il sistema di memorizzazione delle password. Ci eravamo accontentati di salvare questa informazione in chiaro: soluzione sicuramente sconsigliabile dal punto di vista della sicurezza. Agiremo ora sulla direttiva AuthMySQLPwEncryption
che prevede le seguenti opzioni:
- none: password in chiaro, la soluzione momentaneamente adottata nell'esempio;
- crypt: la tipica cifratura UNIX ottenibile con la funzione crypt();
- scrambled: la cifratura propria di MySQL;
- md5: hash MD5;
- aes: la cifratura con AES Advanced Encryption Standard, il formato del campo deve essere un TINYBLOB;
- sha1: la cifratura con Secure Hash Algorihm.
Non abbiamo che l'imbarazzo della scelta, modifichiamo la direttiva dell'esempio così:
AuthMySQLPwEncryption scrambled
In questo caso abbiamo scelto la cifratura nativa di MySQL ottenibile mediante la funzione PASSWORD()
. La query d'inserimento per il nostro utente di prova sarà:
mysql> INSERT INTO user_info VALUES ('testuser', PASSWORD('testpass'));
Altrettanto semplice sarebbe stata l'adozione del parametro crypt, cui corrisponde la funzione ENCRYPT()
, che utilizza la system call Unix crypt()
. Vi rimando alla documentazione ufficiale per ulteriori informazioni relative alle funzioni di cifratura di MySQL.
Una puntualizzazione importante: l'adozione della cifratura consente la protezione dal punto di vista dell'accesso al database. Ovvero una persona non autorizzata, che dovesse visualizzare il contenuto della tabella utenti, non potrebbe interpretare le password. Al momento dell'immissione delle credenziali la connessione tra client e server rimane però in chiaro, a meno che non si ricorra ad una connessione SSL/TLS.
Aggiungiamo i gruppi
Spesso le autorizzazioni sulle risorse vengono gestite più agilmente mediante la creazione di gruppi. Questo permette di non enumerare nella direttiva require
tutti i nomi utente necessari, ma di limitarsi al/ai gruppi di appartenenza:
#elencazione degli utenti require user testuser ermanno mario .... #riferimento semplicemente al gruppo require group amministratori
Modifichiamo la nostra tabella in modo da prevedere un campo per i gruppi ed inseriamo un utente amministratore, chiamato testadmin:
mysql> ALTER TABLE user_info ADD user_group varchar(20) NOT NULL DEFAULT ''; mysql> UPDATE user_info SET user_group = 'utenti' WHERE user_name='testuser'; mysql> INSERT INTO user_info VALUES ('testadmin', PASSWORD('admpass'), 'amministratori');
Modifichiamo il file .htaccess dell'esempio:
AuthName "Area Riservata" AuthType Basic AuthMYSQLEnable on AuthMySQLDB apache_auth AuthMySQLUser apache_user AuthMySQLPassword miosegreto AuthMySQLPwEncryption scrambled AuthMySQLGroupField user_group require group amministratori
AuthMySQLGroupField
ci permette di specificare il nome del campo usato per memorizzare il gruppo d'appartenenza, in modo che Apache possa correttamente effettuare la query. Con require group amministratori
imponiamo che solo gli utenti di tale gruppo possano avere accesso. Le credenziali di testuser verranno rifiutate, contrariamente a quelle di testadmin.
Approfondiamo ancora l'argomento: spesso risulta necessario assegnare ad un utente diversi gruppi. In questo caso per realizzare il legame 1 → N dobbiamo ricorrere ad una tabella esterna. Creiamola, aggiorniamo i dati ed eliminiamo il campo user_group da user_info:
mysql> CREATE TABLE groups( -> user_name VARCHAR(30) NOT NULL, -> user_group VARCHAR(20) NOT NULL, -> PRIMARY KEY (user_name, user_group) -> ); mysql> INSERT INTO groups VALUES ('testuser', 'utenti'); mysql> INSERT INTO groups VALUES ('testadmin', 'amministratori'); mysql> INSERT INTO groups VALUES ('testadmin', 'utenti'); mysql> ALTER TABLE user_info DROP COLUMN user_group;
Ora dobbiamo aggiungere al file .htaccess la seguente direttiva:
AuthMySQLGroupTable groups
per indicare ad Apache la tabella usata per gestire i gruppi. A questo punto mediante la direttiva require group utenti
possiamo creare una directory accessibile contemporaneamente a testuser e testadmin in quanto membri del medesimo gruppo utenti. Possiamo crearne una seconda, riservata a testadmin, con require group amministratori
.
Utilizziamo la nostra tabella utenti
Spesso accade che esista già una tabella utenti, precedentemente creata per altre finalità. Sarebbe comodo utilizzarla senza dover costruire un archivio parallelo. Per fortuna mod_auth_mysql risulta sufficientemente adattabile da permetterci tale operazione. Consideriamo di disporre nel database clients
di una tabella chiamata users
, contenente una serie di campi ben superiore rispetto alla user_info degli esempi iniziali.
Potrebbe essere qualcosa di simile:
mysql> describe users; +-------------+-------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+-------------------+------+-----+---------+----------------+ | UserId | int(8) unsigned | NO | PRI | NULL | auto_increment | | UserLogin | varchar(25) | NO | | NULL | | | UserPass | varchar(32) | NO | | NULL | | | UserActive | tinyint(1) | NO | | 1 | | ...............
I puntini stanno ad indicare che l'elenco dei campi descrittivi potrebbe tranquillamente continuare.
Il problema a questo punto risulta istruire Apache perché possa recuperare i dati corretti:
AuthMYSQLEnable on AuthMySQLDB clients AuthMySQLUser cliuser AuthMySQLPassword miosegreto AuthMySQLPwEncryption md5 AuthMySQLUserTable users AuthMySQLNameField UserLogin AuthMySQLPasswordField UserPass
Niente di più semplice: con AuthMySQLDB
specifichiamo il database, con AuthMySQLUserTable
il nome della tabella su cui effettuare la select, con AuthMySQLNameField
e AuthMySQLPasswordField
il nome dei campi utilizzati per memorizzare username e password. Naturalmente dovremo specificare anche i dati relativi alla connessione al database e l'algoritmo adottato per memorizzare le password.
Possiamo andare oltre: se il campo UserActive viene utilizzato per discriminare utenti attivi da utenti disattivati dobbiamo inserire una condizione nella select effettuata da Apache. Qualcosa di simile a AND UserActive = '1'
. La seguente direttiva fa al caso nostro:
AuthMySQLUserCondition UserActive=1
Se a questo punto consideriamo che AuthMySQLUserTable
consente di specificare più di una tabella, provate ad immaginare come si possa sfruttare AuthMySQLUserCondition
per effettuare delle join. Con un po' di applicazione risulterà possibile far eseguire ad Apache delle query non banali.
Il medesimo discorso vale per la direttiva AuthMySQLGroupTable
con la corrispondente AuthMySQLGroupCondition
.
Conclusioni
In queste pagine abbiamo affrontato le principali direttive messe a disposizione da mod_auth_mysql salvo forse AuthMySQLHost
, che permette la connessione ad un server diverso da localhost. E AuthMySQLPort
per specificare una porta diversa dalla 3306 di default. Le caratteristiche descritte contribuiscono a renderlo uno strumento alternativo a mod_auth. Senza per questo rinunciare alla possibilità di utilizzarli in parallelo. Per ulteriori approfondimenti e dettagli potete fare riferimento alla documentazione ufficiale.