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