Pe scurt: Pentru securitatea sesiunilor PHP contează patru lucruri: setări sigure pentru cookieuri, un ID de sesiune nou după fiecare autentificare, locuri de stocare separate și protejate și durate scurte. Din cauza PHP2Shell, ar trebui setat suplimentar session.upload_progress.enabled=0 pe server, atâta timp cât nu există niciun motiv pentru această funcție.
De ce sunt sesiunile o țintă atractivă
O sesiune reține că un utilizator este autentificat. Tehnic, browserul primește pentru aceasta un ID de sesiune aleatoriu într-un cookie, iar serverul depune datele aferente. Cine cunoaște acest ID este pentru aplicație același utilizator, fără parolă și fără al doilea factor.
Pentru operatori, aceasta înseamnă: o gestionare slabă a sesiunilor poate anula protecția autentificării, chiar dacă parolele și autentificarea cu doi factori sunt implementate curat. Este deosebit de critic la zonele de administrare, conturile de clienți și portalurile cu date cu caracter personal.
Riscurile tipice sunt interceptarea ID-ului de sesiune (session hijacking), strecurarea unui ID cunoscut înainte de autentificare (session fixation) și sesiunile valabile prea mult timp pe calculatoare partajate.
Setări de bază sigure în php.ini
Cele mai importante valori pot fi setate central în php.ini sau pentru fiecare site în pool-ul FPM. În pool folosiți php_admin_value sau php_admin_flag, pentru ca aplicația să nu poată suprascrie valorile.
; Securizarea de bază a sesiunilor
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"
; Indicația PHP2Shell de la php.net, apoi reporniți toți workerii
session.upload_progress.enabled = 0
use_strict_mode face ca PHP să nu preia ID-uri necunoscute, impuse de browser. cookie_httponly împiedică JavaScript să citească ID-ul, ceea ce limitează consecințele Cross-Site Scripting. cookie_secure trimite cookie-ul doar prin HTTPS. SameSite=Lax îngreunează cererile cross-site cu sesiune valabilă; pentru zonele pur administrative poate fi potrivit Strict.
Durata de 1.800 de secunde este o valoare exemplificativă. Stabiliți-o după nevoia de protecție: mai scurtă pentru administrare și date ale clienților, mai lungă doar acolo unde este necesar funcțional.
Rețineți că gc_maxlifetime stabilește doar de când pot fi șterse datele vechi de sesiune. Pe unele distribuții, nu PHP însuși curăță fișierele de sesiune, ci un job periodic de sistem. Verificați atunci ce valoare folosește acest job, pentru ca setarea dumneavoastră să aibă efect real. Pentru deconectarea după inactivitate nu vă bazați, în plus, doar pe funcția de curățare, ci verificați momentul ultimei acțiuni în aplicația însăși, așa cum arată exemplul următor.
Reînnoirea ID-ului de sesiune după autentificare
Cea mai importantă regulă în cod este să regenerați ID-ul de sesiune la fiecare schimbare de autorizare, deci la autentificare, la trecerea într-un mod de administrare și la deconectare. Exemplul următor arată un tipar de bază curat, cu 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; // același mesaj pentru adresă greșită și parolă greșită
}
session_regenerate_id(true); // ID nou, ID-ul vechi se elimină
$_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();
}
În plus, protejați formularele care modifică ceva cu un token CSRF. Acesta este generat cu bin2hex(random_bytes(32)), depus în sesiune și comparat la trimitere cu hash_equals().
Locuri de stocare și separarea clienților
Implicit, PHP depune sesiunile ca fișiere într-un director comun. Pe un server cu mai multe site-uri, aceasta poate însemna că un site compromis poate citi sesiunile altuia. De aceea, fiecărui site îi aparține un pool propriu, cu utilizator propriu și cale de sesiuni proprie.
; în pool-ul FPM al site-ului respectiv
php_admin_value[session.save_path] = /var/lib/php/sessions/kunde-a
Directorul ar trebui să aparțină utilizatorului pool-ului și să aibă drepturile 0700. Cum izolați suplimentar site-urile unele de altele descrie ghidul open_basedir și izolarea pe site. Dacă sesiunile sunt stocate în Redis sau într-o bază de date, se aplică aceleași principii: acces propriu pentru fiecare aplicație, fără acces deschis la rețea.
Securitatea sesiunilor PHP și PHP2Shell
Vulnerabilitățile PHP2Shell CVE-2026-109618 și CVE-2026-109619 se află în PHP însuși. Ca una dintre măsurile provizorii, php.net indică setarea session.upload_progress.enabled=0 în php.ini, în pool-ul FPM sau în configurația serverului și repornirea ulterioară a tuturor workerilor. Explicit nu prin ini_set() sau un .user.ini.
La 23.10.2026 a urmat un anunț despre CVE-2026-115560. O posibilă execuție de cod există acolo doar la o combinație neobișnuită de serializare a sesiunilor, comutare prin ini_set() înainte de session_start() și upload progress activ. Măsura provizorie este aceeași setare, până la următoarea versiune regulată de bugfix.
Această setare este o măsură provizorie și nu o protecție completă. Decisive rămân actualizarea la o versiune PHP corectată și apoi repornirea tuturor workerilor PHP.
După modificare, verificați ce valoare se aplică efectiv. Linia de comandă arată adesea o altă configurație decât serverul web; relevantă este o pagină de verificare protejată, ștearsă apoi din nou, sau însăși configurația pool-ului.
Greșeli tipice la sesiunile din aplicațiile PHP
Multe vulnerabilități nu apar din setări lipsă, ci din modul în care aplicațiile folosesc sesiunile. Aceste tipare ar trebui să le cereți țintit în codul propriu și de la prestatori:
- Prea mult în sesiune: Parolele, datele complete de plată sau seturile mari de date nu aparțin sesiunii. Salvați doar ce este necesar pentru asociere, de exemplu ID-ul utilizatorului și rolurile.
- Drepturi doar din sesiune: Cine este admin ar trebui verificat la acțiunile critice în baza de date. Altfel, drepturile retrase rămân active până la expirarea sesiunii.
- ID de sesiune în adresă: ID-urile din URL-uri ajung în loguri, în istoricul browserului și în indicațiile Referer.
use_only_cookiesșiuse_trans_sid = 0împiedică acest lucru. - Sesiune fără sfârșit: Dacă lipsește un timeout de inactivitate, un admin rămâne autentificat nelimitat pe un calculator partajat.
- HTTPS recunoscut greșit în spatele unui proxy: Dacă criptarea se încheie la load balancer, aplicația trebuie să știe acest lucru. Altfel, poate seta cookieuri fără flagul
Secure.
După un incident de securitate face parte din procedură și eliminarea tuturor sesiunilor existente. Cel mai simplu se întâmplă ștergând conținutul directorului de sesiuni sau schimbând numele sesiunii. Toți utilizatorii trebuie atunci să se autentifice din nou, iar sesiunile furate devin fără valoare.
Cum ajută PHP SOS concret?
- Verificăm setările de sesiune, flagurile de cookie și locurile de stocare de pe toate site-urile unui server și documentăm valorile efectiv active.
- Aplicăm măsurile provizorii PHP2Shell pentru fiecare pool, repornim controlat workerii și dovedim rezultatul.
- Separăm site-urile cu pool-uri și căi de sesiuni proprii, în cadrul securizării serverului.
- Pentru dezvoltările proprii, verificăm în cod autentificarea, deconectarea și schimbarea sesiunii și recomandăm modificări concrete.
Întrebări frecvente
Ce este session hijacking?
La session hijacking, un atacator preia sesiunea unui utilizator autentificat, obținând ID-ul său de sesiune. Cu ID-ul, el este pentru aplicație acel utilizator, fără să cunoască parola. Flagurile sigure de cookie și duratele scurte îngreunează sensibil acest lucru.
De ce trebuie regenerat ID-ul de sesiune după autentificare?
Dacă ID-ul rămâne același înainte și după autentificare, un ID cunoscut în prealabil sau strecurat ar putea rămâne valabil după autentificare. session_regenerate_id(true) generează un ID nou și îl elimină pe cel vechi.
Pot dezactiva session.upload_progress.enabled prin ini_set()?
Nu, potrivit php.net aceasta nu este suficient. Setarea aparține php.ini, pool-ului FPM sau configurației serverului, nu ini_set() sau unui .user.ini. Apoi trebuie repornit toți workerii PHP.
Unde ar trebui stocate fișierele de sesiune?
Într-un director din afara webroot, pe care are voie să-l citească doar utilizatorul pool-ului PHP respectiv. Pe serverele cu mai multe site-uri, fiecare site ar trebui să aibă propriul director de sesiuni.