• Super User

    [Tutorial] La Gestione degli utenti in PHP e MySQL

    [url=http://www.giorgiotave.it/forum/viewtopic.php?p=27409#27409]Cosa è un progetto?

    ** Concetti Base per un Sistema di Log **

    Innanzitutto delineamo le principali caratteristiche dello script:

    1. Comunicazione a connessioni persistenti a MySQL
    2. Utilizzo di sessioni

    Poi, si deve pensare a come deve essere suddiviso:

    1. Registrazione
    2. Log In
    3. Recupero della password
    4. Log Out
    5. Riconoscimento dell'utente
    6. Modifica del profilo

    Però però... Esiste un dilemma!!! 😄 ** le sessioni... ** su file o nel db???

    Certo, su file è più comodo... Però, come disse il nostro amato Rasmus Lerdorf, << Non c'è niente di più sicuro di un database >>. Spetterà a noi, se vogliamo, implementare questo "optional" mediante la creazione di funzioni.


  • Super User

    La Gestione Del Database

    La gestione del database è la prima cosa di cui ci si deve occupare, per poi poter sviluppare uno script ottimale. Bisogna quindi preparare una tabella che deve essere composta almeno di questi elementi:

    1. ID dell'utente -> uID
    2. Nome dell'utente -> uNAME
    3. Password dell'utente -> uPWD

    Che costituiranno dei campi che avranno più o meno questa struttura:

    uID -> ID -> INT(20) - Unsigned - NOT NULL - AutoIncrement - Primary
    uNAME -> Nome -> VARCHAR(20) - NOT NULL
    uPWD -> Password -> VARCHAR(20) - NOT NULL

    **Noterete che... ** uID ha l'attributo AutoIncrement e quindi ad ogni inserimento questo campo si aggiornerà incrementadosi di 1. E' di tipo INT, con una lunghezza di 20 e non è nullo. La utilizzeremo come chiave primaria per una ricerca più rapida con SELECT.

    Questa tabella può essere creata facilmente da una query del genere:

    
    -- Considerando come la tabella verrà chiamata "utenti"
    CREATE TABLE `utenti` &#40;
    
     -- Creiamo in campo per l'ID dell'utente
     `uID` INT&#40; 20 &#41; UNSIGNED NOT NULL AUTO_INCREMENT ,
    
     -- Creiamo il campo per il nome utente
     `uNAME` VARCHAR&#40; 20 &#41; NOT NULL ,
    
     -- Creiamo il campo per la password
     `uPWD` VARCHAR&#40; 20 &#41; NOT NULL ,
    
     -- Stabiliamo come chiave primaria il campo uID
     PRIMARY KEY &#40; `uID` &#41;
    &#41;;
    
    

    Naturalmente se vogliamo possiamo anche aggiungere altri dati, tipo:

    1. Email
    2. Numero di casa
    3. Interessi
    4. Professione
      ...
      ...

    Questi ultimi campi li possiamo mettere tranquillamente insieme agli altri 3 sopra descritti, oppure creare una tabella a parte. Se noi vogliamo mettere tutto in una tabella, dovremo creare una tabella di questo tipo:

    uID -> ID -> INT(20) - Unsigned - NOT NULL - AutoIncrement - Primary
    uNAME -> Nome -> VARCHAR(20) - NOT NULL
    uPWD -> Password -> VARCHAR(20) - NOT NULL
    uMAIL -> Email -> VARCHAR(20) - NOT NULL
    uNUM -> Numero telefonico -> VARCHAR(20) - NOT NULL
    uHOBBIES -> Interessi -> TEXT - NOT NULL
    uJOB -> Professione -> VARCHAR(20) - NOT NULL

    Noterete che:

    1. uID ha l'attributo AutoIncrement e quindi ad ogni inserimento questo campo si aggiornerà incrementadosi di 1. E' di tipo INT, con una lunghezza di 20 e non è nullo. La utilizzeremo come chiave primaria per una ricerca più rapida con SELECT.

    2. Tutti gli altri ( ad eccezione di uHOBBIES ) sono VARCHAR ed hanno una lunghezza massima di 20. Anche questi non sono nulli.

    3. uHOBBIES è di tipo TEXT, e quindi prevede del testo di dimensioni più grandi.

    Perciò... La nostra query dovrebbe essere all'incirca così:

    
    -- Considerando come la tabella verrà chiamata "utenti"
    CREATE TABLE `utenti` &#40;
    
    
     -- Creiamo in campo per l'ID dell'utente
     `uID` INT&#40; 20 &#41; UNSIGNED NOT NULL AUTO_INCREMENT ,
    
     -- Creiamo il campo per il nome utente
     `uNAME` VARCHAR&#40; 20 &#41; NOT NULL ,
    
     -- Creiamo il campo per la password
     `uPWD` VARCHAR&#40; 20 &#41; NOT NULL ,
    
     -- Creiamo il campo per l'Email
     `uMAIL` VARCHAR&#40; 20 &#41; NOT NULL ,
    
     -- Creiamo il campo per il numero telefonico
     `uNUM` VARCHAR&#40; 20 &#41; NOT NULL ,
    
     -- Creiamo il campo per gli interessi
     `uHOBBIES` TEXT NOT NULL ,
    
     -- Creiamo il campo per la professione
     `uJOB` VARCHAR&#40; 20 &#41; NOT NULL ,
    
     -- Stabiliamo come chiave primaria il campo uID
     PRIMARY KEY &#40; `uID` &#41;
    &#41;;
    
    

    Ma se invece vogliamo essere più ordinati e creare una tabella a parte per le cose personali dell'utente? Si può, ma dobbiamo aggiungere il campo uID anche in questa tabella per ricollegarci all'altra tabella; ed avremo quindi uno schema così:

    uID -> ID utente -> INT(20) - Unsigned - NOT NULL - Autoincrement - Primary
    uMAIL -> Email -> VARCHAR(20) - NOT NULL
    uNUM -> Numero telefonico -> VARCHAR(20) - NOT NULL
    uHOBBIES -> Interessi -> TEXT - NOT NULL
    uJOB -> Professione -> VARCHAR(20) - NOT NULL

    La nostra tabella si può creare mediante l'utilizzo di una query così:

    
    -- Considerando come la tabella verrà chiamata "info"
    CREATE TABLE `info` &#40;
    
     -- Creiamo in campo per l'ID dell'utente
     `uID` INT&#40; 20 &#41; UNSIGNED NOT NULL AUTO_INCREMENT ,
    
     -- Creiamo il campo per l'Email
     `uMAIL` VARCHAR&#40; 20 &#41; NOT NULL ,
    
     -- Creiamo il campo per il numero telefonico
     `uNUM` VARCHAR&#40; 20 &#41; NOT NULL ,
    
     -- Creiamo il campo per gli interessi
     `uHOBBIES` TEXT NOT NULL ,
    
     -- Creiamo il campo per la professione
     `uJOB` VARCHAR&#40; 20 &#41; NOT NULL ,
    
     -- Stabiliamo come chiave primaria il campo uID
     PRIMARY KEY &#40; `uID` &#41;
    &#41;;
    
    

    Noterete che... uID ha anche qui l'attributo autoincrement che gli permette di mettersi automaticamente alla pari dell'inserimento nell'altra tabella.

    Naturalmente nel caso di una gestione di questo tipo le query che devono essere eseguite sono ben 2 per 'inserimento: una si occuperà di inserire i dati primari dell'utente, l'altra di inserire le informazioni personali. Per ricevere le informazioni l'operazione diventa un pò più complicata, dato che: Prima ci dobbiamo prendere l'ID dell'utente con una SELECT nella tabella "utenti"; Poi facciamo una SELECT nella tabella "info" inserendo l'ID e prendendoci le informazioni di quel determinato utente.

    Ora che il db è stato preparato ci possiamo anche occupare dello script base, al quale spero ci sia una partecipazione di tutti gli utenti, in modo tale da avere più opinioni, più idee e meno lavoro per tutti 😄

    Vi prego di analizzare bene le parti del sistema che volete approfondire e magari aggiornare prima di intervenire.


  • User Attivo

    ciao pater,

    intervengo per dire la mia sul progetto.

    L'unico appunto che mi viene da fare è sulla divisione in 2 tabelle. Perchè la fai? Come fai notare tu c'è il problema del raddoppio delle query, questo ha anche la spiacevole conseguenza che se malauguratamente la seconda query non va a buon fine (nel caso ad es di un inserimento) avremmo un utente "zoppo" (cioè con solo il record della prima tabella)

    Poi volendo essere pignoli non si rispettano le forme normali, cosa che per praticità si può cmq fare, ma in questo caso non ne vedo i vantaggi

    Altra cosa cosina:

    cosa intendi per "sessioni con file o db" ?

    :ciauz:


  • Super User

    Lo so per la seconda tabella, l'ho messa solo per dare uno spunto in caso di applicazioni più complicate che la richiessero ( in questo caso non ne vale la pena comunque ). Per le sessioni... Come saprai ci sono 2 tipi di sessioni:

    Quelle su File
    Ovvero quelle che ha integrato PHP dalla versione 4, e memorizza le variabili di sessione in una data cartella col nome di sess_ più una stringa esadecimale di 10 caratteri ( se non mi ricordo male ). Questa Gestione però non viene considerata ottimale, perchè è un pò scadente in termini di sicurezza. Per questo oggi molti preferiscono usare le...

    Sessioni Alternative
    Ovvero quelle che possiamo creare NOI, quindi naturalmente non c'è nessun modulo che le rende disponibili. Queste sessioni alternative vengono chiamate così perchè emulano il vero sistema delle sessioni ma le memorizzano in un DB MySQL. Naturalmente la creazione di una classe semplifica molto la gestione di queste sessioni; anche io ne ho creata una per le mie esigenze, se volete la pubblicherò con un piccolo tutorial su questo tipo di sessioni...

    Quindi ero dubbioso sul sistema di gestione della sessione che vorrete utilizzare...


  • User Attivo

    non avevo mai approfondito l'argomento in realtà.....

    cmq sembra abbastanza interessante.

    Per quel poco che ho letto, se devi gestirele tu, tanto vale usare il db, xò a quanto pare hai più esperienza in questo campo, quindi a te la scelta! 🙂


  • Super User

    Mi aggrego al progetto....prontissimo a programmare!

    So fare tutta la parte che non comprende le sessioni.

    Di sessioni ne so molto poco!

    😄


  • Super User

    @Tuonorosso said:

    Mi aggrego al progetto....prontissimo a programmare!

    So fare tutta la parte che non comprende le sessioni.

    Di sessioni ne so molto poco!

    :Driki, io ho solo dato le linee guida, ora tocca anche a voi aiutarci!! Sennò che progetto è? 😄

    Grande Tuonorosso, vedi se c'è qualcosa in particolare che intendi sviluppare, vediamo quanti saranno disposti a collaborare!!!


  • User Attivo

    @PaTeR said:

    riki, io ho solo dato le linee guida, ora tocca anche a voi aiutarci!! Sennò che progetto è? 😄

    purtroppo ora non ho assolutamente il tempo 😞 quindi mi limito a dare consigli in generale......

    preferisco fare lavorare voi!! 😄


  • Super User

    @riky78 said:

    purtroppo ora non ho assolutamente il tempo 😞 quindi mi limito a dare consigli in generale......

    preferisco fare lavorare voi!! 😄 :fumato:


  • Bannato Super User

    @PaTeR said:

    Grande Tuonorosso, vedi se c'è qualcosa in particolare che intendi sviluppare, vediamo quanti saranno disposti a collaborare!!!

    Conta pure anche me, solo un paio di giorni per sbrigare due cosette e poi mi dedico anche a questo progetto con quel poco che so.

    Io ho sviluppato su di un sito una cosa del genere, ma è moolto elementare, posso partire da lì per vedere come si può migliorare.

    😉


  • Super User

    @PaTeR said:

    Naturalmente nel caso di una gestione di questo tipo le query che devono essere eseguite sono ben 2 per 'inserimento: una si occuperà di inserire i dati primari dell'utente, l'altra di inserire le informazioni personali. Per ricevere le informazioni l'operazione diventa un pò più complicata, dato che: Prima ci dobbiamo prendere l'ID dell'utente con una SELECT nella tabella "utenti"; Poi facciamo una SELECT nella tabella "info" inserendo l'ID e prendendoci le informazioni di quel determinato utente.

    Perché due SELECT separate quando si può fare con una sola?

    Aggiungo anche che l'idea di gestire gli ID di sessione col database è ottima, sopratutto per ragioni di sicurezza, tuttavia implementare questa soluzione per poi archiviare in chiaro le password è un po' come sbarrare tutte le finestre di casa lasciando però aperta la porta principale.

    Ben vengano gli ID di sessione nel database, ma la stessa scrupolosità e attenzione alla sicurezza dovrebbe imporre l'esclusione di password in chiaro nel database.

    Inoltre, quando l'utente inserisce la password nel form, come viene inviata la medesima al server? In chiaro? Non si fa! 🙂

    E se io scopro in qualche modo l'ID di sessione di un utente e provo a collegarmi al server spedendo quell'ID, come fa il sistema ad evitare di scambiarmi per quell'utente?

    Non vorrei aver equivocato le finalità del progetto. Se si tratta di un semplice script per il riconoscimento degli utenti, allora non è il caso di preoccuparsi. Ma se una maggiore sicurezza rientra nelle caratteristiche che si desidera implementare, allora bisogna pensare ad un bel po' di cose oltre a quelle già citate.


  • Super User

    @LowLevel said:

    @PaTeR said:

    Naturalmente nel caso di una gestione di questo tipo le query che devono essere eseguite sono ben 2 per 'inserimento: una si occuperà di inserire i dati primari dell'utente, l'altra di inserire le informazioni personali. Per ricevere le informazioni l'operazione diventa un pò più complicata, dato che: Prima ci dobbiamo prendere l'ID dell'utente con una SELECT nella tabella "utenti"; Poi facciamo una SELECT nella tabella "info" inserendo l'ID e prendendoci le informazioni di quel determinato utente.

    Perché due SELECT separate quando si può fare con una sola?

    Aggiungo anche che l'idea di gestire gli ID di sessione col database è ottima, sopratutto per ragioni di sicurezza, tuttavia implementare questa soluzione per poi archiviare in chiaro le password è un po' come sbarrare tutte le finestre di casa lasciando però aperta la porta principale.

    Ben vengano gli ID di sessione nel database, ma la stessa scrupolosità e attenzione alla sicurezza dovrebbe imporre l'esclusione di password in chiaro nel database.

    Inoltre, quando l'utente inserisce la password nel form, come viene inviata la medesima al server? In chiaro? Non si fa! 🙂

    E se io scopro in qualche modo l'ID di sessione di un utente e provo a collegarmi al server spedendo quell'ID, come fa il sistema ad evitare di scambiarmi per quell'utente?

    Non vorrei aver equivocato le finalità del progetto. Se si tratta di un semplice script per il riconoscimento degli utenti, allora non è il caso di preoccuparsi. Ma se una maggiore sicurezza rientra nelle caratteristiche che si desidera implementare, allora bisogna pensare ad un bel po' di cose oltre a quelle già citate.
    Intendi usare Javascript per codificare la password?

    Comunque fai degli esempi per favore, sinceramente non ci avevo mai pensato a quello che dici tu... Per il momento non ho nessuna idea tranne l'uso di una connessione SSL...


  • Super User

    Intendi usare Javascript per codificare la password?

    Sì. Questo non elimina del tutto il problema ma lo riduce almeno nell'80% dei casi, ad occhio e croce.

    Se vuoi, spiego. Ma ti anticipo che sarò costretto a scrivere un bel po' di roba, per spiegare come funziona (il codice finale sarà invece pochissimo), quindi decidi tu se è il caso di introdurre queste caratteristiche al progetto o se queste spiegazioni lo complicherebbero troppo.


  • User Attivo

    @LowLevel said:

    E se io scopro in qualche modo l'ID di sessione di un utente e provo a collegarmi al server spedendo quell'ID, come fa il sistema ad evitare di scambiarmi per quell'utente?

    E' un pò difficile fare una cosa del genere, però credo che in questo caso il sistema penserà che tu sei quell'utente per cui avrai accesso.

    Mi sono gia dedicato alla realizzazione dell'autenticazione utente. Però girato e rigirato nell'ID di sessione le password sono in chiaro. Per cui quello che ho fatto io non può essere considerata una soluzione professionale.


  • Super User

    @LowLevel said:

    Perché due SELECT separate quando si può fare con una sola?

    Concordo pienamente anche perchè se fai un semplice schema E-R ti accorgi subito che non ha senso avere 2 tabelle.

    @LowLevel said:

    Aggiungo anche che l'idea di gestire gli ID di sessione col database è ottima, sopratutto per ragioni di sicurezza, tuttavia implementare questa soluzione per poi archiviare in chiaro le password è un po' come sbarrare tutte le finestre di casa lasciando però aperta la porta principale.

    Se non sbaglio in MySQL c'è un bel comandino MD5(password) per inserire la pw criptata nel db...

    @LowLevel said:

    Inoltre, quando l'utente inserisce la password nel form, come viene inviata la medesima al server? In chiaro? Non si fa! 🙂

    Vero vero... 😄

    @LowLevel said:

    E se io scopro in qualche modo l'ID di sessione di un utente e provo a collegarmi al server spedendo quell'ID, come fa il sistema ad evitare di scambiarmi per quell'utente?

    mmmm, si potrebbe fare il controllo incrociato ip-sessione.
    Ossia, arrivo, mi loggo, inserisco sessione e ip nel db e poi ad ogni pagina controllo questa coppia...
    sono :fumato: ?

    @LowLevel said:

    Non vorrei aver equivocato le finalità del progetto. Se si tratta di un semplice script per il riconoscimento degli utenti, allora non è il caso di preoccuparsi. Ma se una maggiore sicurezza rientra nelle caratteristiche che si desidera implementare, allora bisogna pensare ad un bel po' di cose oltre a quelle già citate.

    Ovviamente non si vuol fare uno scrippettino scolastico...sarebbe troppo semplice 😄


  • Super User

    @LowLevel said:

    Intendi usare Javascript per codificare la password?

    Se vuoi, spiego. Ma ti anticipo che sarò costretto a scrivere un bel po' di roba, per spiegare come funziona (il codice finale sarà invece pochissimo), quindi decidi tu se è il caso di introdurre queste caratteristiche al progetto o se queste spiegazioni lo complicherebbero troppo. :sbav: :sbav: :sbav: :sbav: :sbav: :sbav: :sbav: :sbav: :sbav: :sbav: :sbav: :sbav: :sbav: :sbav: :sbav:


  • Super User

    @linus said:

    @LowLevel said:

    E se io scopro in qualche modo l'ID di sessione di un utente e provo a collegarmi al server spedendo quell'ID, come fa il sistema ad evitare di scambiarmi per quell'utente?

    E' un pò difficile fare una cosa del genere

    A seconda dei casi, può rivelarsi facilissimo.

    Ad esempio, molte installazioni si PHP sono configurate in modo da accodare automaticamente ad ogni URL un parametro con l'ID di sessione nel caso in cui l'utente non abbia i cookie attivi.

    Nel momento in cui gli ID di sessione appaiono negli URL, basta seguire un qualsiasi link verso un altro sito con il referer attivo nel browser per far memorizzare nei log del sito di destinazione l'ID usato dall'utente sul sito da cui proviene.

    Concordo pienamente anche perchè se fai un semplice schema E-R ti accorgi subito che non ha senso avere 2 tabelle.

    Io mi riferivo al fatto che si può usare una singola SELECT anche in presenza di due tabelle.

    Se non sbaglio in MySQL c'è un bel comandino MD5(password) per inserire la pw criptata nel db...

    La soluzione passa infatti da algoritmi per il calcolo di checksum, come md5(), ma c'è modo di aumentare ulteriormente la sicurezza facendo qualcosa in più oltre alla semplice memorizzazione dell'md5() della password.

    Ossia, arrivo, mi loggo, inserisco sessione e ip nel db e poi ad ogni pagina controllo questa coppia...

    Infatti si fa così. Ma per coprire anche i casi in cui più utenti siano dietro lo stesso firewall/proxy e presentino quindi lo stesso IP, può essere una buona idea aggiungere qualche controllo sull'user-agent. Ma solo in alcuni casi.

    Ovviamente non si vuol fare uno scrippettino scolastico...sarebbe troppo semplice

    Io sto attendendo un nulla-osta perché è la prima volta che leggo di questi vostri progetti e non ho idea di come funzionino. Cioè, non so se bisogna attenersi alla traccia fornita da PaTeR oppure se può essere ampliata. Per tale ragione, aspetto chiarimenti da PaTeR, che mi pare sia il promotore del progetto.


  • Super User

    in un caso studio si cerca di trovare assieme la soluzione migliore rimanendo in bilico tra la funzionalità del progetto e gli esempi "a scopo didattico". 🙂

    la relazione uno a uno tra due tabelle, personalmente, viene usata quando mi trovo a dover interrogare una tabella da più di 20 campi (numero indicativo) dove i campi che effettivamente mi interessano sono pochissimi (2 o 3) ed il numero di record è potenzialmente elevato.

    Per dare un esempio concreto su cui ragionare senza inventarsi un progetto ad hoc per spiegare il concetto, l'anagrafica utenti calza a pennello.

    Se l'anagrafica utenti avesse tutti i campi necessari per memorizzare vita morte e miracoli degli utenti (indirizzi, vari numeri di telefono, vari siti email e chi più ne ha più ne metta) e si raggiunge una dimensione di 30-40 campi per fare semplicemente il login non voglio far girare un tabellone così grande ed in questo caso separo le due tabelle in una striminzita che quindi sarà snella e veloce e l'altra (più grossa e lenta) che verrà interrogata solo quando saranno effettivamente necessarie tutte le info.

    pro e contro:

    pro:
    una maggiore velocità di esecuzione quando faccio il login perchè devo interrogare una tabellina di pochi campi e ben indicizzata (gli indici li vedremo quando analiziamo come fare le tabelle nel dettaglio).

    contro:
    una lentezza leggermente maggiore quando il motore deve estrarre i dati completi del singolo utente in quanto dovrà risolvere un join.

    l'interrogazione nel primo caso sarà:
    select * from utenti where (condizioni)

    nel secondo sarà:
    select (dati che mi interessano) from utenti, utenti_info_aggiuntive where utenti.id = utenti_info_aggiuntive.id_utente where (condizioni)

    pareri? :ciauz:


  • Super User

    @Tymba said:

    pareri?

    Sì. Che oltre al motivo indicato da te, la soluzione multi-tabella è generalmente indicata per progetti destinati a possibili ampliamenti.

    Se un sito possiede forum, blog e sondaggi e tutti e tre questi servizi beneficiano dell'autenticazione degli utenti, ha senso creare una tabella con le informazioni generiche e valevoli per tutti i servizi (username, password, IP, ecc.) ed una tabella in più per ciascuno dei servizi esistenti, contenente solo le informazioni-utente relative al servizio al quale la tabella è dedicata.

    Se un domani sarà necessario aggiungere un guestbook o un modulo per spedirsi messaggi privati, ci si limita a creare una nuova tabella per il nuovo servizio, senza toccare nessuna di quelle già esistenti.

    Separare le informazioni relative all'autenticazione da quelle aggiuntive e non essenziali all'autenticazione è sempre una buona idea. Anche perché la tabella con le informazioni per l'autenticazione subisce query continue, anche una ad ogni caricamento di pagina, a seconda dell'implementazione. Meglio che sia piccola, come dicevi tu.


  • Super User

    @ LowLevel: E' vero che io ho scritto il post, ma il progetto era già stato studiato tra i mods, quindi non seguire solo me, ma anche tutti gli altri mods.

    Io ho solo dato una mano su come realizzare il DB, voi potete seguire quella traccia oppure potete modificarla a vostro piacimento.

    Preferirei che questo progetto avesse anche finalità didattiche, come se fosse una vera pillola, in quanto sarebbe più facile da comprendere e da modificare o estendere. Quindi non sarebbe male se scriveste un pò di righe in più nel vostro post anche per spiegare cosa state facendo e come lo state facendo.

    Per il fatto della password mandata in chiaro, sarebbe una buona cosa se ci spiegassi come fare per mandarla già criptata, perchè ripeto: tutto ciò che può riguardare un sistema di gestioni utenti è meglio inserirlo. E la sicurezza mi sembra rientrare in questi 😄

    Non fatevi venire strane idee su come un moderatore vedrà il vostro messaggio: al massimo vi aiuterà a migliorarlo, di certo non vi :frust: 😄