Server-rum med blå lys
← Tilbage til GuidesMellem - Avanceret

PHP Sessions

Komplet guide til sessions i PHP — fra grundlæggende session_start() til sikker session-håndtering i produktion.

Foto: Taylor Vick / Unsplash

Hvad er en session?

En session lader dig gemme data om en bruger på tværs af flere requests. PHP genererer et unikt session-id, sender det som cookie til klienten, og bruger det til at hente brugerens data fra serverens session-storage på efterfølgende requests.

Sessions bruges typisk til login-status, indkøbskurve, CSRF-tokens og brugerpræferencer. PHP har indbygget session-håndtering via $_SESSION superglobal, men korrekt konfiguration er afgørende for sikkerhed — default-indstillingerne er ikke gode nok i produktion.

Grundlæggende session-brug

Kald session_start() før du sender noget output — ellers kan PHP ikke sætte session-cookien. Derefter bruges $_SESSION som et almindeligt array.

<?php
// Start eller genoptag session - SKAL stå før output
session_start();

// Skriv til session
$_SESSION['user_id'] = 42;
$_SESSION['email'] = 'henrik@example.dk';
$_SESSION['login_time'] = time();

// Læs fra session (på næste request)
if (isset($_SESSION['user_id'])) {
    echo "Logget ind som user {$_SESSION['user_id']}";
}

// Slet en enkelt værdi
unset($_SESSION['login_time']);

// Tøm hele sessionen
$_SESSION = [];

// Korrekt logout - destroy session helt
session_unset();
session_destroy();

// Slet også session-cookien hos klienten
if (ini_get('session.use_cookies')) {
    $params = session_get_cookie_params();
    setcookie(
        session_name(),
        '',
        time() - 42000,
        $params['path'],
        $params['domain'],
        $params['secure'],
        $params['httponly']
    );
}

Sikker session-konfiguration

PHPs default session-indstillinger er ikke produktionsklar. Sæt disse værdier i php.ini eller via session_start()-options.

<?php
// Anbefalet sikker konfiguration
session_start([
    // Cookie sikkerhed
    'cookie_secure'   => true,      // Kun HTTPS
    'cookie_httponly' => true,      // Ingen JavaScript-adgang
    'cookie_samesite' => 'Strict',  // CSRF-beskyttelse
    'cookie_lifetime' => 0,         // Slettes ved browser-luk

    // Session-id sikkerhed
    'use_strict_mode'   => true,    // Afvis ukendte session-ids
    'use_only_cookies'  => true,    // Aldrig session-id i URL
    'sid_length'        => 48,      // Længere ID (default 26)
    'sid_bits_per_character' => 6,  // Højere entropi

    // Garbage collection
    'gc_maxlifetime' => 1800,       // 30 minutter
    'gc_probability' => 1,
    'gc_divisor'     => 100,

    // Cache headers
    'cache_limiter' => 'nocache',
]);

use_strict_mode er kritisk: uden den accepterer PHP session-ids fra brugeren, hvilket åbner for session fixation-angreb.

Beskyttelse mod session hijacking

Regenerér session-id ved login og ved priviligerede handlinger. Det forhindrer at en stjålet session-id forbliver gyldig efter login.

<?php

function loginUser(int $userId): void {
    // Regenerér session-id ved login
    session_regenerate_id(true); // true = slet gammel session

    $_SESSION['user_id'] = $userId;
    $_SESSION['login_time'] = time();
    $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
    $_SESSION['ua_hash'] = hash(
        'sha256',
        $_SERVER['HTTP_USER_AGENT'] ?? ''
    );
}

function validateSession(): bool {
    // Krav: bruger skal være logget ind
    if (!isset($_SESSION['user_id'])) {
        return false;
    }

    // Tjek IP og User-Agent matcher
    $currentUaHash = hash(
        'sha256',
        $_SERVER['HTTP_USER_AGENT'] ?? ''
    );

    if ($_SESSION['ua_hash'] !== $currentUaHash) {
        // Sandsynligvis hijacked
        session_destroy();
        return false;
    }

    // Forny session-id hver 15. minut
    if (time() - ($_SESSION['last_regen'] ?? 0) > 900) {
        session_regenerate_id(true);
        $_SESSION['last_regen'] = time();
    }

    return true;
}

Custom session handlers (Redis-eksempel)

Default gemmer PHP sessions som filer på disk. I distribuerede setups (flere webservere) skal sessions deles — typisk via Redis eller en database. Implementér SessionHandlerInterface.

<?php

class RedisSessionHandler implements SessionHandlerInterface {
    public function __construct(
        private Redis $redis,
        private int $lifetime = 1800,
    ) {}

    public function open(string $path, string $name): bool {
        return true;
    }

    public function close(): bool {
        return true;
    }

    public function read(string $id): string|false {
        $data = $this->redis->get("session:{$id}");
        return $data === false ? '' : $data;
    }

    public function write(string $id, string $data): bool {
        return $this->redis->setex(
            "session:{$id}",
            $this->lifetime,
            $data
        );
    }

    public function destroy(string $id): bool {
        $this->redis->del("session:{$id}");
        return true;
    }

    public function gc(int $maxlifetime): int|false {
        // Redis håndterer expiration automatisk via TTL
        return 0;
    }
}

// Registrér handler før session_start()
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$handler = new RedisSessionHandler($redis);
session_set_save_handler($handler, true);

session_start([
    'cookie_secure' => true,
    'cookie_httponly' => true,
    'cookie_samesite' => 'Strict',
    'use_strict_mode' => true,
]);

Redis er førstevalget for session-storage i moderne PHP-stacks: hurtig, distribueret og med automatisk expiration via TTL.

Sessions vs JWT — hvornår vælger du hvad?

Brug Sessions når

  • • Du bygger en traditionel webapp med server-rendered HTML
  • • Du har brug for at invalidere et login med det samme
  • • Brugerdata kan ændres mellem requests (fx roller, permissions)
  • • Du kan dele state mellem servere via Redis

Brug JWT når

  • • Du bygger en stateless REST API
  • • Mobile apps eller SPAs er klienter
  • • Du har microservices der skal validere tokens uafhængigt
  • • Du accepterer at logout først virker når token udløber

Best Practices

+Sæt altid cookie_secure, cookie_httponly og cookie_samesite=Strict i produktion
+Aktivér use_strict_mode for at afvise session-ids genereret af klienten
+Kald session_regenerate_id(true) ved login og ved skift af privilegier
+Brug aldrig session-id i URLs - sæt use_only_cookies = true
+Gem sessions i Redis eller database, ikke filer, hvis du har flere webservere
+Tjek IP og User-Agent som ekstra signal mod hijacking (men ikke som eneste check)
+Sæt en kort gc_maxlifetime (15-30 minutter) for følsomme applikationer
+Aldrig gem store mængder data i $_SESSION - ID er bedre, hent data fra database
+Husk at lukke sessionen med session_write_close() i lange scripts (frigør locks)