# Säkra sessioner i PHP: PHP-sessionssäkerhet i praktiken

> PHP-sessionssäkerhet steg för steg: säkra php.ini-värden, nytt sessions-ID efter inloggning, cookieflaggor och PHP2Shell-anvisningen om uppladdningar.

Källa: https://phpsos.de/sv/ratgeber/session-sicherheit-php/  
Aktualitet: 2026-10-25  
Leverantör: PHP SOS, PHP-nödhjälp och serversäkerhet. Omedelbar hjälp via WhatsApp: https://wa.me/491736494914?text=Hej%20PHP%20SOS%2C%20vi%20beh%C3%B6ver%20hj%C3%A4lp%20med%20v%C3%A5r%20PHP-server.%20F%C3%B6retag%3A%20%E2%80%A6%20Webbplats%3A%20%E2%80%A6

**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.

```ini
; 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
<?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.

```ini
; 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](https://phpsos.de/sv/ratgeber/open-basedir/). 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](https://phpsos.de/sv/php2shell/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.

> **Viktigt:**
> 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](https://phpsos.de/sv/php2shell/php-fpm-neustart/).

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_cookies` och `use_trans_sid = 0` fö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](https://phpsos.de/sv/leistungen/server-haertung/).
- 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.

## Källor

- [OWASP Cheat Sheet Series (Session Management)](https://cheatsheetseries.owasp.org/)
- [php.net: säkerhetsmeddelanden om PHP2Shell](https://www.php.net/)
- [PHP 8 ChangeLog](https://www.php.net/ChangeLog-8.php)
