Menu

Sessioni e cookie in PHP: session_start e setcookie

Chiama session_start() in cima a ogni pagina, poi salva i valori in $_SESSION['key']; PHP li tiene sul server e riconosce il visitatore tramite un cookie. setcookie('name', 'value', $options) salva un piccolo valore nel browser, che si rilegge da $_COOKIE. Impara login, logout, messaggi flash, opzioni dei cookie e impostazioni di sicurezza.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Chiama session_start() in cima a una pagina, poi leggi e scrivi $_SESSION['key'] come qualsiasi array: PHP lo salva sul server e lo ritrova alla richiesta successiva del visitatore. Per un piccolo valore che il browser deve conservare, chiama setcookie('name', 'value', time() + 86400) e rileggilo nelle richieste successive da $_COOKIE['name'].

<?php
session_start();                        // before any output
setcookie('theme', 'dark', time() + 60 * 60 * 24 * 30);   // 30 days, also before output

$_SESSION['visits'] = ($_SESSION['visits'] ?? 0) + 1;
$theme = $_COOKIE['theme'] ?? 'light';  // set on an earlier visit
echo "You have opened this page {$_SESSION['visits']} times.";

Sessioni e cookie hanno senso solo su più richieste HTTP di un browser, quindi gli esempi che ne hanno bisogno sono codice semplice. La logica intorno a essi è normale PHP su array, e quella parte viene eseguita qui: in ogni blocco eseguibile, $session fa le veci di $_SESSION, e ogni chiamata è una richiesta.

Come funziona una sessione PHP

  1. Il primo session_start() crea un id casuale, lo invia al browser in un cookie chiamato PHPSESSID e ti dà un $_SESSION vuoto.
  2. Alla fine della richiesta PHP salva $_SESSION sul server (di default in file, oppure in Redis o in un database).
  3. Alla richiesta successiva il browser rimanda il cookie, session_start() legge l'id e carica l'array salvato.

Il browser non vede mai i dati, solo l'id. Ecco perché l'id dell'utente autenticato va in $_SESSION e mai in un tuo cookie: un visitatore può modificare i propri cookie con qualsiasi valore, ma non può modificare $_SESSION.

Login e logout di un utente con una sessione

Fare il login significa: verificare la password, rigenerare l'id di sessione, salvare l'id utente. Il logout è il contrario, e ha tre passaggi.

<?php
// login.php
session_start();

$user = find_user_by_email($_POST['email'] ?? '');
if ($user && password_verify($_POST['password'] ?? '', $user['password_hash'])) {
    session_regenerate_id(true);        // new id after login: stops session fixation
    $_SESSION['user_id'] = $user['id'];
    header('Location: /dashboard.php');
    exit;
}
$error = 'Wrong email or password.';
<?php
// any protected page
session_start();
if (!isset($_SESSION['user_id'])) {
    header('Location: /login.php');
    exit;
}
<?php
// logout.php
session_start();
$_SESSION = [];                                     // 1. forget the data

$p = session_get_cookie_params();                   // 2. delete the cookie
setcookie(session_name(), '', time() - 3600, $p['path'], $p['domain'], $p['secure'], $p['httponly']);

session_destroy();                                  // 3. delete the stored session
header('Location: /');
exit;

session_regenerate_id(true) conta più di quanto sembri. Se un attaccante riesce a far usare a una vittima un id di sessione che conosce (per esempio tramite un link), e l'id resta lo stesso dopo il login, anche l'attaccante risulta autenticato. Un id nuovo al login chiude quella falla. La verifica della password è trattata nella pagina su password_hash.

Far scadere una sessione dopo un periodo di inattività

La pulizia propria di PHP (session.gc_maxlifetime, 1440 secondi di default) avviene in modo casuale e non è un timeout affidabile. Salva l'ora dell'ultima richiesta e verificala tu. Il blocco usa timestamp fissi così puoi vedere ogni caso; cambia $timeout o gli orari.

L'intervallo dalle 09:05 alle 09:16:40 è di 700 secondi, sotto il limite di 15 minuti, quindi l'utente resta autenticato. L'intervallo successivo è di 1000 secondi, quindi la sessione viene azzerata.

Messaggi flash

Un messaggio flash viene impostato in una richiesta e mostrato una volta nella successiva, tipicamente "Saved." dopo un redirect. Salvalo nella sessione, poi leggilo e rimuovilo in un solo passo:

setcookie() accetta il nome, il valore e un array di opzioni (PHP 7.3 e successivi). Imposta queste quattro opzioni su ogni cookie, a meno che tu non abbia un motivo per non farlo:

<?php
setcookie('theme', 'dark', [
    'expires'  => time() + 60 * 60 * 24 * 30,   // 30 days; 0 means "until the browser closes"
    'path'     => '/',                          // send it on every page, not just this folder
    'secure'   => true,                         // only over HTTPS
    'httponly' => true,                         // JavaScript cannot read it
    'samesite' => 'Lax',                        // not sent on most cross-site requests
]);

$theme = $_COOKIE['theme'] ?? 'light';

Due cose fanno inciampare:

  • $_COOKIE non cambia nella stessa richiesta. Contiene ciò che il browser ha inviato con questa richiesta. Un cookie che imposti ora compare in $_COOKIE alla prossima.
  • I cookie sono intestazioni, quindi setcookie() deve essere eseguito prima di qualsiasi output, esattamente come session_start() e header().

Per eliminare un cookie, impostalo di nuovo con lo stesso nome e path e una scadenza nel passato: setcookie('theme', '', ['expires' => time() - 3600, 'path' => '/']).

Il valore di un cookie è una stringa di circa 4 KB al massimo, quindi salva i dati strutturati come JSON, e tratta ciò che torna come non affidabile: il visitatore può modificarlo. Non usare mai unserialize() su un cookie: può creare oggetti delle tue classi ed eseguirne i metodi magici, mentre json_decode() restituisce sempre e solo array, stringhe, numeri, booleani, null o stdClass.

La pagina su json_encode tratta i flag. Per qualsiasi cosa più grande di pochi valori brevi, tieni i dati nella sessione (o in un database) e salva nel cookie solo una chiave.

Token "ricordami"

Un cookie "ricordami" non deve contenere l'id utente né l'hash della password. Salva nel cookie un lungo token casuale e nel database solo il suo hash, e confronta con hash_equals():

I valori cambiano a ogni esecuzione, perché random_bytes() è casuale. Se il database viene sottratto, gli hash sono inutili senza i cookie, e hash_equals() confronta in tempo costante così il controllo non rivela informazioni sui tempi.

Sessione ($_SESSION)Cookie ($_COOKIE)
SalvataSul serverNel browser
Il visitatore può leggerla o modificarlaNo (solo l'id)Sì
DimensioneLimitata dal tuo spazio di archiviazioneCirca 4 KB per cookie
DuraFinché il browser si chiude o la sessione scadeFino alla sua scadenza expires
Usala perUtente autenticato, carrello, token CSRF, messaggi flashTema, lingua, scelta sul consenso, token "ricordami"

Errore comune: headers already sent

session_start(), setcookie() e header() inviano tutte intestazioni HTTP, e le intestazioni si possono inviare solo prima del corpo. Qualsiasi output precedente, anche una riga vuota prima di <?php in un file incluso, produce:

Warning: session_start(): Session cannot be started after headers have already been sent in /var/www/page.php on line 3
Warning: Cannot modify header information - headers already sent by (output started at /var/www/page.php:2) in /var/www/page.php on line 4

Il secondo messaggio ti dice dove è iniziato l'output (page.php:2): vai a quella riga e rimuovi l'output, oppure sposta session_start() sopra di essa. Nei file che contengono solo PHP, omettere il ?> di chiusura impedisce che spazi bianchi residui dopo di esso diventino output.

Domande frequenti

Qual è la differenza tra una sessione e un cookie in PHP?

Un cookie viene salvato nel browser del visitatore e inviato con ogni richiesta, quindi il visitatore può leggerlo e modificarlo. Una sessione salva i suoi dati sul server; il browser tiene solo un id di sessione casuale in un cookie (PHPSESSID). Metti in $_SESSION tutto ciò che deve essere affidabile, come l'id dell'utente autenticato.

Come distruggo una sessione al logout in PHP?

Svuota i dati, elimina il cookie di sessione e distruggi la sessione salvata: $_SESSION = [];, poi setcookie(session_name(), '', time() - 3600, '/'), poi session_destroy();. Fai poi un redirect così la pagina successiva riparte da zero.

Come elimino un cookie in PHP?

Impostalo di nuovo con lo stesso nome, percorso e dominio e una scadenza nel passato: setcookie('theme', '', ['expires' => time() - 3600, 'path' => '/']);. Fai anche unset($_COOKIE['theme']) se il resto dello script corrente lo legge, perché $_COOKIE cambia solo alla richiesta successiva.

Quanto dura una sessione PHP?

Di default il cookie di sessione dura finché il browser non viene chiuso (session.cookie_lifetime = 0) e il server può eliminare i dati di sessione dopo 1440 secondi (24 minuti) di inattività (session.gc_maxlifetime). Per un timeout fisso, salva nella sessione l'ora dell'ultima richiesta e verificala tu.

Perché session_start() dice headers already sent?

L'id di sessione viaggia in un'intestazione di cookie, e le intestazioni devono essere inviate prima di qualsiasi output. Un echo, dell'HTML, o anche una riga vuota o un BOM prima di <?php inviano prima l'output. Chiama session_start() proprio in cima allo script, prima che venga stampato qualsiasi cosa.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA