Kort sagt: För PHP-sessionssäkerhet räknas fyra saker: säkra cookieinställningar, ett nytt sessions-ID efter varje inloggning, åtskilda och skyddade lagringsplatser samt korta livslängder. På grund av PHP2Shell bör dessutom session.upload_progress.enabled=0 vara satt på servern, så länge det inte finns något skäl för den funktionen.
Varför sessioner är ett lönsamt mål
En session minns att en användare är inloggad. Tekniskt får webbläsaren för det ett slumpmässigt sessions-ID i en cookie, och servern lagrar tillhörande data. Den som känner till ID:t är för applikationen samma användare, utan lösenord och utan andra faktor.
För driftansvariga betyder det: En svag sessionshantering kan sätta inloggningsskyddet ur spel, även om lösenord och tvåfaktorsinloggning är rent genomförda. Särskilt kritiskt är det i adminområden, kundkonton och portaler med personuppgifter.
De typiska riskerna är att sessions-ID:t snappas upp (session hijacking), att ett känt ID undanskuffas före inloggningen (session fixation) och att sessioner gäller för länge på delade datorer.
Säkra grundinställningar i php.ini
De viktigaste värdena kan sättas centralt i php.ini eller per webbplats i FPM-poolen. I poolen använder ni php_admin_value eller php_admin_flag, så att applikationen inte kan skriva över värdena.
; Grundläggande sessionshärdning
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"
; PHP2Shell-anvisning från php.net, starta därefter om alla workers
session.upload_progress.enabled = 0
use_strict_mode ser till att PHP inte tar över okända ID:n som webbläsaren föreskriver. cookie_httponly hindrar JavaScript från att läsa ID:t, vilket begränsar följderna av cross-site scripting. cookie_secure skickar cookien bara via HTTPS. SameSite=Lax försvårar cross-site-förfrågningar med giltig session; för rena adminområden kan Strict passa.
Livslängden på 1 800 sekunder är ett exempelvärde. Fastställ den efter skyddsbehovet: kortare för administration och kunduppgifter, längre bara där det är sakligt nödvändigt.
Observera att gc_maxlifetime bara anger från vilken tidpunkt gamla sessionsdata får raderas. På vissa distributioner är det inte PHP självt som städar utan ett regelbundet systemjobb som rensar sessionsfilerna. Kontrollera då vilket värde det jobbet använder så att er inställning verkligen får verkan. Förlita er dessutom inte enbart på städfunktionen för utloggning vid inaktivitet, utan kontrollera tidpunkten för senaste åtgärd i själva applikationen, som exemplet nedan visar.
Förnya sessions-ID efter inloggning
Den viktigaste regeln i koden är att skapa sessions-ID på nytt vid varje byte av behörighet, alltså vid inloggning, vid byte till ett adminläge och vid utloggning. Följande exempel visar ett rent grundmönster med 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; // samma meddelande för fel adress och fel lösenord
}
session_regenerate_id(true); // nytt ID, förkasta det gamla
$_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();
}
Skydda dessutom formulär som ändrar något med en CSRF-token. Den skapas med bin2hex(random_bytes(32)), läggs i sessionen och jämförs vid inskickandet med hash_equals().
Lagringsplatser och klientseparation
Som standard lägger PHP sessioner som filer i en gemensam katalog. På en server med flera webbplatser kan det betyda att en komprometterad webbplats kan läsa en annans sessioner. Därför hör till varje webbplats en egen pool med egen användare och egen sessionssökväg.
; i FPM-poolen för respektive webbplats
php_admin_value[session.save_path] = /var/lib/php/sessions/kunde-a
Katalogen bör ägas av poolens användare och ha rättigheterna 0700. Hur ni dessutom skärmar av webbplatser från varandra beskriver guiden open_basedir och isolering per webbplats. Lagras sessioner i Redis eller en databas gäller samma principer: egen åtkomst per applikation, ingen öppen nätverksåtkomst.
PHP-sessionssäkerhet och PHP2Shell
PHP2Shell-sårbarheterna CVE-2026-109618 och CVE-2026-109619 finns i PHP självt. Som en av de tillfälliga åtgärderna anger php.net att sätta session.upload_progress.enabled=0 i php.ini, i FPM-poolen eller i serverkonfigurationen och därefter starta om alla workers. Uttryckligen inte via ini_set() eller en .user.ini.
Den 23 oktober 2026 följde ett meddelande om CVE-2026-115560. En möjlig kodkörning föreligger där bara vid en ovanlig kombination av sessionsserialisering, omställning via ini_set() före session_start() och aktivt upload progress. Åtgärden är samma inställning, fram till nästa reguljära bugfix-release.
Den här inställningen är en tillfällig åtgärd och inget fullständigt skydd. Avgörande förblir uppdateringen till en åtgärdad PHP-version och den efterföljande omstarten av alla PHP-workers.
Kontrollera efter ändringen vilket värde som faktiskt gäller. Kommandoraden visar ofta en annan konfiguration än webbservern; tillförlitligt är en skyddad kontrollsida som raderas igen efteråt, eller själva poolkonfigurationen.
Typiska fel med sessioner i PHP-applikationer
Många sårbarheter uppstår inte på grund av saknade inställningar utan genom hur applikationer använder sessioner. Fråga riktat efter de här mönstren i den egna koden och hos tjänsteleverantörer:
- För mycket i sessionen: Lösenord, fullständiga betalningsuppgifter eller stora datamängder hör inte hemma i sessionen. Spara bara det som behövs för tilldelningen, till exempel användar-ID och roller.
- Rättigheter enbart från sessionen: Vem som är admin bör vid kritiska åtgärder kontrolleras mot databasen. Annars förblir indragna rättigheter verksamma tills sessionen löper ut.
- Sessions-ID i adressen: ID:n i URL:er hamnar i loggar, webbläsarhistorik och referer-uppgifter.
use_only_cookiesochuse_trans_sid = 0förhindrar det. - Sessionen tar aldrig slut: Saknas en timeout vid inaktivitet förblir en admin på en delad dator inloggad obegränsat.
- HTTPS bakom en proxy känns inte igen rätt: Slutar krypteringen vid lastbalanseraren måste applikationen veta det. Annars sätter den kanske cookies utan
Secure-flaggan.
Efter en säkerhetsincident hör det dessutom till att förkasta alla befintliga sessioner. Det görs enklast genom att radera innehållet i sessionskatalogen eller ändra sessionsnamnet. Alla användare måste då logga in på nytt, och stulna sessioner blir värdelösa.
Hur hjälper PHP SOS konkret?
- Vi granskar sessionsinställningar, cookieflaggor och lagringsplatser på alla webbplatser på en server och dokumenterar de värden som faktiskt gäller.
- Vi sätter de tillfälliga åtgärderna mot PHP2Shell per pool, startar om workers kontrollerat och styrker resultatet.
- Vi separerar webbplatser med egna pooler och sessionssökvägar inom ramen för serverhärdningen.
- För egenutvecklade lösningar granskar vi inloggning, utloggning och sessionsbyte i koden och rekommenderar konkreta ändringar.
Vanliga frågor
Vad är session hijacking?
Vid session hijacking tar en angripare över en inloggad användares session genom att komma över dennes sessions-ID. Med ID:t är angriparen för applikationen den användaren, utan att känna till lösenordet. Säkra cookieflaggor och korta livslängder försvårar det avsevärt.
Varför måste sessions-ID skapas på nytt efter inloggning?
Förblir ID:t detsamma före och efter inloggningen kan ett tidigare känt eller undanskuffat ID fortsätta gälla efter inloggningen. session_regenerate_id(true) skapar ett nytt ID och förkastar det gamla.
Kan jag stänga av session.upload_progress.enabled med ini_set()?
Nej, det räcker enligt php.net inte. Inställningen hör hemma i php.ini, FPM-poolen eller serverkonfigurationen, inte i ini_set() eller en .user.ini. Därefter måste alla PHP-workers startas om.
Var bör sessionsfiler lagras?
I en katalog utanför webbroten som bara användaren för respektive PHP-pool får läsa. På servrar med flera webbplatser bör varje webbplats ha en egen sessionskatalog.