Na kratko: Pri varnosti sej v PHP štejejo štiri stvari: varne nastavitve piškotkov, nov ID seje po vsaki prijavi, ločena in zaščitena mesta shranjevanja ter kratke življenjske dobe. Zaradi PHP2Shell naj bo poleg tega na strani strežnika nastavljen session.upload_progress.enabled=0, dokler za to funkcijo ni razloga.
Zakaj so seje vredna tarča
Seja si zapomni, da je uporabnik prijavljen. Tehnično brskalnik zanjo prejme naključen ID seje v piškotku, strežnik pa shrani pripadajoče podatke. Kdor ta ID pozna, je za aplikacijo isti uporabnik, brez gesla in brez drugega faktorja.
Za upravljavce to pomeni: šibko upravljanje sej lahko izniči zaščito prijave, tudi če so gesla in dvofaktorska prijava čisto izvedena. Posebej kritično je to pri skrbniških območjih, računih strank in portalih z osebnimi podatki.
Tipična tveganja so prestrezanje ID-ja seje (session hijacking), podtikanje znanega ID-ja pred prijavo (session fixation) in predolgo veljavne seje na skupnih računalnikih.
Varne osnovne nastavitve v php.ini
Najpomembnejše vrednosti lahko nastavite osrednje v php.ini ali za vsako spletno stran v poolu FPM. V poolu uporabite php_admin_value ali php_admin_flag, da aplikacija vrednosti ne more prepisati.
; Osnovno utrjevanje sej
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"
; Opozorilo PHP2Shell s php.net, nato znova zaženite vse workerje
session.upload_progress.enabled = 0
use_strict_mode poskrbi, da PHP ne prevzame neznanih ID-jev, ki jih predpiše brskalnik. cookie_httponly preprečuje, da bi JavaScript prebral ID, kar omeji posledice Cross-Site Scriptinga. cookie_secure piškotek pošilja samo prek HTTPS. SameSite=Lax otežuje zahteve med spletnimi stranmi z veljavno sejo; za zgolj skrbniška območja je lahko primeren Strict.
Življenjska doba 1.800 sekund je vzorčna vrednost. Določite jo glede na potrebo po zaščiti: krajša za upravo in podatke strank, daljša samo tam, kjer je to strokovno potrebno.
Upoštevajte, da gc_maxlifetime določa le, od kdaj se stari podatki sej smejo brisati. Pri nekaterih distribucijah datoteke sej ne počisti PHP sam, ampak redno sistemsko opravilo. Tedaj preverite, katero vrednost to opravilo uporablja, da vaša nastavitev dejansko učinkuje. Za odjavo po nedejavnosti se poleg tega ne zanašajte samo na funkcijo čiščenja, ampak čas zadnjega dejanja preverjajte v sami aplikaciji, kot kaže naslednji primer.
Nov ID seje po prijavi
Najpomembnejše pravilo v kodi je, da se ID seje ustvari znova ob vsaki spremembi pooblastila, torej ob prijavi, ob prehodu v skrbniški način in ob odjavi. Naslednji primer prikazuje čist osnovni vzorec s 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; // isto sporočilo za napačen naslov in napačno geslo
}
session_regenerate_id(true); // nov ID, stari zavrzite
$_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();
}
Dodatno obrazce, ki kaj spremenijo, zaščitite z žetonom CSRF. Ustvari se z bin2hex(random_bytes(32)), shrani v seji in ob oddaji primerja s hash_equals().
Mesta shranjevanja in ločitev najemnikov
PHP seje privzeto shranjuje kot datoteke v skupni mapi. Na strežniku z več spletnimi stranmi to lahko pomeni, da kompromitirana spletna stran lahko bere seje druge. Zato k vsaki spletni strani sodi lasten pool z lastnim uporabnikom in lastno potjo sej.
; v poolu FPM zadevne spletne strani
php_admin_value[session.save_path] = /var/lib/php/sessions/kunde-a
Mapa naj pripada uporabniku poola in ima pravice 0700. Kako spletne strani dodatno ločite med seboj, opisuje vodnik open_basedir in izolacija po spletnih straneh. Če se seje shranjujejo v Redisu ali podatkovni zbirki, veljajo ista načela: lasten dostop za vsako aplikacijo, brez odprtega omrežnega dostopa.
Varnost sej v PHP in PHP2Shell
Ranljivosti PHP2Shell CVE-2026-109618 in CVE-2026-109619 sta v samem PHP. Kot enega od vmesnih ukrepov php.net navaja, da se session.upload_progress.enabled=0 nastavi v php.ini, v poolu FPM ali v konfiguraciji strežnika in nato znova zaženejo vsi workerji. Izrecno ne prek ini_set() ali .user.ini.
Dne 23. 10. 2026 je sledilo opozorilo o CVE-2026-115560. Morebitno izvajanje kode tam obstaja samo pri nenavadni kombinaciji serializacije sej, preklopa prek ini_set() pred session_start() in aktivnega upload progress. Rešitev je ista nastavitev, do naslednje redne izdaje s popravki napak.
Ta nastavitev je vmesni ukrep in ne popolna zaščita. Odločilna ostaja posodobitev na odpravljeno različico PHP in nato ponovni zagon vseh workerjev PHP.
Po spremembi preverite, katera vrednost dejansko velja. Ukazna vrstica pogosto prikaže drugačno konfiguracijo kot spletni strežnik; zanesljiva je zaščitena, nato znova izbrisana preizkusna stran ali sama konfiguracija poola.
Tipične napake pri sejah v aplikacijah PHP
Številne ranljivosti ne nastanejo zaradi manjkajočih nastavitev, ampak zaradi načina, kako aplikacije seje uporabljajo. Te vzorce ciljno preverite v lastni kodi in pri ponudnikih storitev:
- Preveč v seji: Gesla, popolni plačilni podatki ali veliki nabori podatkov ne sodijo v sejo. Shranite samo, kar je potrebno za dodelitev, na primer ID uporabnika in vloge.
- Pravice samo iz seje: Kdo je skrbnik, je treba pri kritičnih dejanjih preveriti glede na podatkovno zbirko. Sicer odvzete pravice ostanejo veljavne do izteka seje.
- ID seje v naslovu: ID-ji v URL-jih končajo v dnevnikih, zgodovini brskalnika in navedbah referer.
use_only_cookiesinuse_trans_sid = 0to preprečita. - Brez konca seje: Če manjka časovna omejitev nedejavnosti, skrbnik na skupnem računalniku ostane prijavljen neomejeno.
- HTTPS za proxyjem napačno prepoznan: Če se šifriranje konča na load balancerju, mora aplikacija to vedeti. Sicer piškotke morda nastavi brez zastavice
Secure.
Po varnostnem incidentu sodi zraven tudi zavrnitev vseh obstoječih sej. Najlažje to storite tako, da izbrišete vsebino mape sej ali spremenite ime seje. Vsi uporabniki se morajo potem znova prijaviti, ukradene seje pa so brez vrednosti.
Kako PHP SOS pomaga konkretno?
- Nastavitve sej, zastavice piškotkov in mesta shranjevanja preverimo na vseh spletnih straneh strežnika in dokumentiramo dejansko veljavne vrednosti.
- Vmesne ukrepe PHP2Shell uvedemo za vsak pool, nadzorovano znova zaženemo workerje in rezultat dokažemo.
- Spletne strani ločimo z lastnimi pooli in potmi sej v okviru utrjevanja strežnika.
- Za lastne razvoje preverimo prijavo, odjavo in menjavo seje v kodi ter priporočimo konkretne spremembe.
Pogosta vprašanja
Kaj je session hijacking?
Pri session hijackingu napadalec prevzame sejo prijavljenega uporabnika, tako da pridobi njen ID seje. Z ID-jem je za aplikacijo ta uporabnik, ne da bi poznal geslo. Varne zastavice piškotkov in kratke življenjske dobe to občutno otežijo.
Zakaj je treba ID seje po prijavi ustvariti znova?
Če je ID pred prijavo in po njej enak, lahko prej znan ali podtaknjen ID po prijavi še naprej velja. session_regenerate_id(true) ustvari nov ID in stari zavrže.
Ali lahko session.upload_progress.enabled izklopim z ini_set()?
Ne, po php.net to ne zadošča. Nastavitev sodi v php.ini, pool FPM ali konfiguracijo strežnika, ne v ini_set() ali .user.ini. Nato je treba znova zagnati vse workerje PHP.
Kje naj se shranjujejo datoteke sej?
V mapi zunaj korena spleta, ki jo sme brati samo uporabnik zadevnega poola PHP. Na strežnikih z več spletnimi stranmi naj ima vsaka spletna stran lastno mapo sej.