# Cum securizați sesiunile în PHP: securitatea sesiunilor PHP în practică

> Securitatea sesiunilor PHP pas cu pas: valori sigure în php.ini, ID de sesiune nou după autentificare, flaguri de cookie și indicația PHP2Shell despre încărcări.

Sursa: https://phpsos.de/ro/ratgeber/session-sicherheit-php/  
Stadiu: 25.10.2026  
Furnizor: PHP SOS, Asistență de urgență PHP și securitatea serverelor. Ajutor imediat pe WhatsApp: https://wa.me/491736494914?text=Bun%C4%83%20ziua%2C%20PHP%20SOS%2C%20avem%20nevoie%20de%20ajutor%20cu%20serverul%20nostru%20PHP.%20Firma%3A%20%E2%80%A6%20Site%3A%20%E2%80%A6

**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.

```ini
; 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
<?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.

```ini
; î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](https://phpsos.de/ro/ratgeber/open-basedir/). 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](https://phpsos.de/ro/php2shell/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.

> **Important:**
> 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](https://phpsos.de/ro/php2shell/php-fpm-neustart/).

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` și `use_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](https://phpsos.de/ro/leistungen/server-haertung/).
- 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.

## Surse

- [OWASP Cheat Sheet Series (Session Management)](https://cheatsheetseries.owasp.org/)
- [php.net: anunțuri de securitate despre PHP2Shell](https://www.php.net/)
- [PHP 8 ChangeLog](https://www.php.net/ChangeLog-8.php)
