In breve: per la sicurezza delle sessioni PHP contano quattro cose: impostazioni sicure dei cookie, un nuovo ID di sessione a ogni accesso, luoghi di archiviazione separati e protetti e durate brevi. A causa di PHP2Shell dovrebbe inoltre essere impostato lato server session.upload_progress.enabled=0, finché non c'è motivo di usare questa funzione.
Perché le sessioni sono un bersaglio appetibile
Una sessione memorizza che un utente è connesso. Tecnicamente il browser riceve a questo scopo un ID di sessione casuale in un cookie e il server conserva i dati corrispondenti. Chi conosce questo ID è, per l'applicazione, lo stesso utente, senza password e senza secondo fattore.
Per i gestori significa che una gestione debole delle sessioni può vanificare la protezione del login, anche se password e autenticazione a due fattori sono realizzate correttamente. È particolarmente critico per aree admin, account clienti e portali con dati personali.
I rischi tipici sono la sottrazione dell'ID di sessione (session hijacking), l'inserimento di un ID noto prima del login (session fixation) e le sessioni valide troppo a lungo su computer condivisi.
Impostazioni di base sicure nel php.ini
I valori più importanti si possono impostare in modo centralizzato nel php.ini o, per ogni sito, nel pool FPM. Nel pool usi php_admin_value o php_admin_flag, in modo che l'applicazione non possa sovrascrivere i valori.
; Hardening di base delle sessioni
session.use_strict_mode = 1
session.use_only_cookies = 1
session.use_trans_sid = 0
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = "Lax"
session.cookie_lifetime = 0
session.gc_maxlifetime = 1800
session.name = "APPSESSID"
; Avviso PHP2Shell di php.net, poi riavviare tutti i worker
session.upload_progress.enabled = 0
use_strict_mode fa sì che PHP non accetti ID sconosciuti imposti dal browser. cookie_httponly impedisce a JavaScript di leggere l'ID, il che limita le conseguenze del cross-site scripting. cookie_secure invia il cookie solo tramite HTTPS. SameSite=Lax rende più difficili le richieste cross-site con sessione valida; per le aree puramente amministrative può essere adatto Strict.
La durata di 1.800 secondi è un valore di esempio. La stabilisca in base al bisogno di protezione: più breve per amministrazione e dati dei clienti, più lunga solo dove è necessario per ragioni operative.
Tenga presente che gc_maxlifetime stabilisce soltanto da quando i vecchi dati di sessione possono essere cancellati. Su alcune distribuzioni non è PHP stesso a ripulire i file di sessione, ma un job di sistema periodico. Verifichi allora quale valore usa quel job, in modo che la Sua impostazione abbia davvero effetto. Per la disconnessione per inattività non si affidi inoltre alla sola funzione di pulizia, ma controlli nell'applicazione stessa il momento dell'ultima azione, come mostrato nell'esempio seguente.
Rinnovare l'ID di sessione dopo il login
La regola più importante nel codice è rigenerare l'ID di sessione a ogni cambio di autorizzazione, cioè al login, al passaggio a una modalità admin e al logout. L'esempio seguente mostra uno schema di base corretto con password_verify():
<?php
declare(strict_types=1);
session_start();
function anmelden(PDO $db, string $email, string $passwort): bool
{
$stmt = $db->prepare('SELECT id, passwort_hash FROM benutzer WHERE email = :email');
$stmt->execute(['email' => $email]);
$nutzer = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$nutzer || !password_verify($passwort, $nutzer['passwort_hash'])) {
return false; // stesso messaggio per indirizzo errato e password errata
}
session_regenerate_id(true); // nuovo ID, scartare il vecchio
$_SESSION['benutzer_id'] = (int) $nutzer['id'];
$_SESSION['letzte_aktion'] = time();
return true;
}
function pruefeLeerlauf(int $sekunden = 900): void
{
if (isset($_SESSION['letzte_aktion']) && time() - $_SESSION['letzte_aktion'] > $sekunden) {
abmelden();
return;
}
$_SESSION['letzte_aktion'] = time();
}
function abmelden(): void
{
$_SESSION = [];
$p = session_get_cookie_params();
setcookie(session_name(), '', [
'expires' => time() - 3600,
'path' => $p['path'],
'domain' => $p['domain'],
'secure' => $p['secure'],
'httponly' => $p['httponly'],
'samesite' => $p['samesite'],
]);
session_destroy();
}
In aggiunta protegga i moduli che modificano qualcosa con un token CSRF. Viene generato con bin2hex(random_bytes(32)), salvato nella sessione e confrontato all'invio con hash_equals().
Luoghi di archiviazione e separazione dei clienti
Per impostazione predefinita PHP salva le sessioni come file in una directory condivisa. Su un server con più siti può significare che un sito compromesso possa leggere le sessioni di un altro. Perciò a ogni sito spetta un pool proprio con utente proprio e percorso di sessione proprio.
; nel pool FPM del rispettivo sito
php_admin_value[session.save_path] = /var/lib/php/sessions/kunde-a
La directory dovrebbe appartenere all'utente del pool e avere i permessi 0700. Come isolare ulteriormente i siti l'uno dall'altro è descritto nella guida open_basedir e isolamento per sito. Se le sessioni sono salvate in Redis o in un database, valgono gli stessi principi: accesso proprio per ogni applicazione, nessun accesso di rete aperto.
Sicurezza delle sessioni PHP e PHP2Shell
Le vulnerabilità PHP2Shell CVE-2026-109618 e CVE-2026-109619 si trovano in PHP stesso. Come una delle misure provvisorie php.net indica di impostare session.upload_progress.enabled=0 nel php.ini, nel pool FPM o nella configurazione del server e poi di riavviare tutti i worker. Espressamente non tramite ini_set() o un .user.ini.
Il 23/10/2026 è seguito un avviso su CVE-2026-115560. Lì una possibile esecuzione di codice esiste solo con una combinazione insolita di serializzazione della sessione, cambio tramite ini_set() prima di session_start() e upload progress attivo. La misura è la stessa impostazione, fino al prossimo release regolare di correzione.
Questa impostazione è una misura provvisoria e non una protezione completa. Resta decisivo l'aggiornamento a una versione corretta di PHP e il successivo riavvio di tutti i worker PHP.
Dopo la modifica verifichi quale valore valga effettivamente. La riga di comando mostra spesso una configurazione diversa da quella del server web; è affidabile una pagina di verifica protetta e poi cancellata, oppure la configurazione del pool stessa.
Errori tipici con le sessioni nelle applicazioni PHP
Molte debolezze non nascono da impostazioni mancanti, ma dal modo in cui le applicazioni usano le sessioni. Questi schemi vanno chiesti in modo mirato nel codice proprio e ai fornitori di servizi:
- Troppo nella sessione: password, dati di pagamento completi o grandi insiemi di dati non vanno nella sessione. Salvi solo ciò che serve per l'assegnazione, ad esempio ID utente e ruoli.
- Diritti solo dalla sessione: chi è admin dovrebbe essere verificato con il database nelle azioni critiche. Altrimenti i diritti revocati restano efficaci fino alla scadenza della sessione.
- ID di sessione nell'indirizzo: gli ID negli URL finiscono nei log, nella cronologia del browser e nelle indicazioni referer.
use_only_cookieseuse_trans_sid = 0lo impediscono. - Nessuna fine della sessione: senza un timeout di inattività, un admin su un computer condiviso resta connesso a tempo indeterminato.
- HTTPS non riconosciuto dietro un proxy: se la cifratura termina sul load balancer, l'applicazione deve saperlo. Altrimenti potrebbe impostare i cookie senza flag
Secure.
Dopo un incidente di sicurezza fa inoltre parte del lavoro scartare tutte le sessioni esistenti. Il modo più semplice è cancellare il contenuto della directory delle sessioni o cambiare il nome della sessione. Tutti gli utenti devono allora accedere di nuovo e le sessioni rubate non valgono più nulla.
Come aiuta concretamente PHP SOS?
- Verifichiamo impostazioni di sessione, flag dei cookie e luoghi di archiviazione su tutti i siti di un server e documentiamo i valori effettivamente in vigore.
- Applichiamo le misure provvisorie PHP2Shell per ogni pool, riavviamo i worker in modo controllato e dimostriamo il risultato.
- Separiamo i siti con pool e percorsi di sessione propri nell'ambito dell'hardening del server.
- Per gli sviluppi propri verifichiamo login, logout e cambio di sessione nel codice e raccomandiamo modifiche concrete.
Domande frequenti
Che cos'è il session hijacking?
Nel session hijacking un aggressore prende in carico la sessione di un utente connesso ottenendone l'ID di sessione. Con l'ID, per l'applicazione è quell'utente senza conoscerne la password. Flag dei cookie sicuri e durate brevi lo rendono molto più difficile.
Perché l'ID di sessione deve essere rigenerato dopo il login?
Se l'ID resta lo stesso prima e dopo il login, un ID noto in precedenza o fatto inserire di nascosto potrebbe continuare a valere dopo l'accesso. session_regenerate_id(true) genera un nuovo ID e scarta il vecchio.
Posso disattivare session.upload_progress.enabled con ini_set()?
No, secondo php.net non basta. L'impostazione va nel php.ini, nel pool FPM o nella configurazione del server, non in ini_set() o in un .user.ini. Poi tutti i worker PHP devono essere riavviati.
Dove vanno salvati i file di sessione?
In una directory fuori dalla webroot, leggibile solo dall'utente del rispettivo pool PHP. Sui server con più siti ogni sito dovrebbe avere una propria directory di sessione.