PHP, session GC & timeout autenticazione

Se sei un programmatore PHP sicuramente ti sarà capitato di costruire un sito web con autenticazione.

L’autenticazione implica una sessione lato server e tale sessione, di solito, in siti ben fatti, implica un timeout dopo il quale viene eseguito un logout automatico per inattività dell’utente.

L’autenticazione e il timeout devono essere implementati da te con i meccanismi che preferisci, mentre la sessione di solito è gestita da PHP mediante la classica chiamata alla funzione session_start().

 

Il metodo più usato per sapere se una sessione è valida è usare una $variabile della stessa sessione che contiene un timestamp aggiornato ogni volta che l’utente esegue una qualunque operazione.

Ad ogni operazione dell’utente, il codice controlla se il valore del timestamp va oltre il timeout stabilito, e se questo accade, la sessione viene cancellata: questo costringerà l’utente a ri-autenticarsi come previsto.

Che c’è di speciale?

Niente di particolare finora… ma non tutti hanno ben presente che PHP possiede dei meccanismi di garbage collection per le sessioni, e ancora meno noto è il meccanismo con cui operano.

Che c’entra la GC per le sessioni con l’autenticazione e il timeout?

Se il timeout previsto per la sessione della tua applicazione è abbastanza lungo, diciamo più di 24 minuti, potresti avere un problema.

Indipendentemente da quanto sia perfetto il meccanismo di timeout che hai concepito, non funzionerà bene su intervalli lunghi, semplicemente perché la GC per le sessioni di PHP cancellerà la sessione del tuo utente “forse” ogni 24 minuti o più!

Eh già… non lo sapevi? Cerchiamo di capire bene il perché…

I dettagli dell’automatismo della garbage collection per le sessioni sono contenuti nella configurazione di PHP, in particolare in questi 3 parametri e con questi valori di default (che possono cambiare a seconda della distribuzione utilizzata):

session.gc_probability “1”
session.gc_divisor “100” Available since PHP 4.3.2.
session.gc_maxlifetime “1440”

Il primo problema è sapere QUANDO la GC agirà: i primi due valori ci dicono che avverrà con una probabilità di 1 su 100. Questo lancio di dadi eseguito da PHP avverrà ogni volta che la funzione session_start() viene chiamata dal nostro codice (la documentazione asserisce “…on every session initialization.”).

Il terzo valore session.gc_maxlifetime ci dice che, quando avverrà la GC, cancellerà tutte le sessioni che sono più vecchie di 1440 secondi (24 minuti). Più vecchie significa che il timestamp del file della sessione (se la sessione è su file) dovrà essere più vecchio di 1440 secondi rispetto al momento in cui viene controllato. Se il controllo sul timestamp passa, PHP aggiorna comunque il timestamp del file (in particolare quello modified), anche se non viene alterata alcuna variabile in sessione. Questo potrebbe farti pensare che è inutile impostare un meccanismo di timeout proprietario nel codice PHP e lasciar fare tutto alla GC: quasi vero.

E’ un errore NON inserire un meccanismo di timeout nella sessione ed affidarsi alla GC di PHP per il logoff su inattività degli utenti, per vari motivi:
1) Visto che la GC viene eseguita casualmente, la sessione di un utente potrebbe rimanere attiva più tempo di quanto sia il valore di session.gc_maxlifetime, specialmente se il sito ha pochi visitatori.

2) Visualizzare all’utente un messaggio per fargli capire che la sessione è “scaduta” sarebbe più complicato perché il tuo codice non saprebbe distinguere facilmente un nuovo utente da uno con sessione scaduta: entrambi non avrebbero più la sessione! L’utente vedrebbe di nuovo la pagina di login come se l’avesse richiamata la prima volta. Potresti impostare un cookie che non scade o salvare altre informazioni dell’utente fuori dalla sessione… ma a mio vedere ti complicheresti solo la vita.

3) Come per il punto 2, non sarebbe facile registrare l’evento di timeout/logoff dell’utente su un log, fatto importante ai fini dei meccanismi di autenticazione.

Ma torniamo al tuo meccanismo di timeout: se il valore del timeout di sessione voluto da te è entro i 24 minuti, il tuo meccanismo di logoff su inattività agirà sempre prima della GC e quindi tutto funzionerà a dovere. Non importa in questo caso la probabilità, in qualunque momento venga eseguita la GC, la tua sessione non sarà mai cancellata perché non durerebbe comunque più di 24 minuti visto che il tuo codice per il timeout agisce prima della GC di PHP.

Se invece vuoi un timeout di 60 minuti devi aumentare il valore di session.gc_maxlifetime e portarlo almeno a 3600 impedendo di fatto a PHP di cancellare la sessione anzitempo, lasciandolo fare a te.

Se non lo fai lasciando tutto ai valori di default, avrai l’1% di probabilità che la sessione sia cancellata se si rimane inattivi per più di 24 minuti! La probabilità che avvenga aumenterà con il numero di visite al sito e il tuo perfetto meccanismo di timeout non entrerà mai in funzione perché lo farà prima la GC di PHP.

 

La particolarità di Debian

Sui sistemi operativi Debian come ad es. Ubuntu, è stata disabilitata la GC delle sessioni di PHP a livello di codice dell’interprete PHP, vedi il bug report al riguardo. Il tutto è stato sostituito da uno script bash lanciato da cron che ogni 30 minuti (per esattezza al minuto 9 e al minuto 39 di ogni ora) esegue la GC al posto di PHP onorando fortunatamente il parametro session.gc_maxlifetime inserito nel file php.ini.

Questa modifica rende la casualità dell’esecuzione della GC una costante nel tempo rendendo inutili le modifiche ai parametri session.gc_divisor e session.gc_probability.

comments powered by HyperComments