Implement session expiration enforcement: clear remember cookies and redirect to login on session expiry; update header script for daily re-login requirement.

This commit is contained in:
2026-09-09 16:49:29 +02:00
parent 1487b680f1
commit 1b6c8e4afd
7 changed files with 71 additions and 76 deletions
+42 -70
View File
@@ -76,6 +76,9 @@ function ensure_remember_token_columns(PDO $db): void {
$result = $db->query("SHOW COLUMNS FROM `users` LIKE 'remember_token'");
if (!$result || $result->rowCount() === 0) {
$db->exec("ALTER TABLE `users` ADD COLUMN `remember_token` VARCHAR(64) NULL AFTER `backup_codes`, ADD COLUMN `remember_token_expires_at` DATETIME NULL AFTER `remember_token`");
} else {
// Maak alle bestaande actieve tokens direct leeg zodat geen enkele oude persistente browser-sessie blijft bestaan
$db->exec("UPDATE `users` SET `remember_token` = NULL, `remember_token_expires_at` = NULL WHERE `remember_token` IS NOT NULL");
}
} catch (Throwable $e) {
// Stil negeren indien al aanwezig of tijdens migraties
@@ -119,35 +122,23 @@ function ensure_theme_column(PDO $db): void {
}
/**
* Genereert en bewaart een dagspecifieke login token in MariaDB en plaatst een veilige cookie.
* Verloopt altijd dezelfde nacht om 23:59:59 zodat sessies niet meerdere dagen blijven staan.
* Verouderde functie voor persistent login.
* Bewust geneutraliseerd: cookies worden direct gewist en geen tokens meer aangemaakt,
* zodat gebruikers iedere dag om 23:59:59 worden uitgelogd en daadwerkelijk opnieuw moeten inloggen.
*/
function issue_remember_token(int $userId): string {
$rawToken = bin2hex(random_bytes(32));
$tokenHash = hash('sha256', $rawToken);
$midnightTs = strtotime('today 23:59:59');
$expiresAt = date('Y-m-d 23:59:59', $midnightTs);
$db = get_db_connection();
ensure_remember_token_columns($db);
$stmt = $db->prepare("UPDATE users SET remember_token = ?, remember_token_expires_at = ? WHERE id = ?");
$stmt->execute([$tokenHash, $expiresAt, $userId]);
$isSecure = is_https_request();
setcookie('fociz_remember', $rawToken, [
'expires' => $midnightTs, // Verloopt vannacht om 23:59:59
'path' => '/',
'secure' => $isSecure,
'httponly' => true,
'samesite' => 'Lax'
]);
return $rawToken;
clear_remember_cookie();
try {
$db = get_db_connection();
ensure_remember_token_columns($db);
$stmt = $db->prepare("UPDATE users SET remember_token = NULL, remember_token_expires_at = NULL WHERE id = ?");
$stmt->execute([$userId]);
} catch (Throwable $e) {}
return '';
}
/**
* Verwijdert de persistente remember cookie
* Verwijdert de persistente remember cookie uit de browser
*/
function clear_remember_cookie(): void {
$isSecure = is_https_request();
@@ -158,11 +149,15 @@ function clear_remember_cookie(): void {
'httponly' => true,
'samesite' => 'Lax'
]);
if (isset($_COOKIE['fociz_remember'])) {
unset($_COOKIE['fociz_remember']);
}
}
/**
* Haalt de huidige ingelogde gebruiker op uit de database.
* Garandeert dat de sessie strikt om 23:59:59 gereset wordt en nooit meerdere dagen blijft staan.
* Bevat GEEN persistente cookies of auto-login bypass: de gebruiker moet iedere dag daadwerkelijk opnieuw inloggen.
*/
function get_logged_in_user(): ?array {
start_secure_session();
@@ -170,18 +165,22 @@ function get_logged_in_user(): ?array {
$today = date('Y-m-d');
$now = time();
// Verwijder altijd eventuele oude of achtergebleven fociz_remember cookies
if (isset($_COOKIE['fociz_remember'])) {
clear_remember_cookie();
}
// Controleer of de sessie van een eerdere dag is of voorbij 23:59:59
$isExpired = false;
if (!empty($_SESSION['auth_date']) && $_SESSION['auth_date'] !== $today) {
$_SESSION = [];
clear_remember_cookie();
if (session_status() === PHP_SESSION_ACTIVE) {
@session_regenerate_id(true);
}
return null;
$isExpired = true;
}
if (!empty($_SESSION['session_expires_at']) && $now > (int)$_SESSION['session_expires_at']) {
$_SESSION = [];
$isExpired = true;
}
if ($isExpired) {
$_SESSION = ['session_was_expired' => true];
clear_remember_cookie();
if (session_status() === PHP_SESSION_ACTIVE) {
@session_regenerate_id(true);
@@ -189,11 +188,10 @@ function get_logged_in_user(): ?array {
return null;
}
$db = get_db_connection();
ensure_remember_token_columns($db);
// 1. Controleer actieve PHP sessie
// Controleer actieve PHP sessie (uitsluitend de sessie van vandaag is geldig)
if (!empty($_SESSION['user_id']) && !empty($_SESSION['is_2fa_verified'])) {
$db = get_db_connection();
ensure_remember_token_columns($db);
$stmt = $db->prepare("SELECT id, name, email, role, two_factor_enabled, created_at FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch();
@@ -207,38 +205,6 @@ function get_logged_in_user(): ?array {
clear_remember_cookie();
}
// 2. Controleer dagspecifieke cookie (uitsluitend geldig voor vandaag tot 23:59:59)
if (!empty($_COOKIE['fociz_remember'])) {
$rawToken = trim($_COOKIE['fociz_remember']);
$tokenHash = hash('sha256', $rawToken);
$stmt = $db->prepare("
SELECT id, name, email, role, two_factor_enabled, created_at
FROM users
WHERE remember_token = ? AND remember_token_expires_at > NOW()
");
$stmt->execute([$tokenHash]);
$user = $stmt->fetch();
if ($user) {
// Herstel PHP sessie voor de huidige werkdag
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['name'];
$_SESSION['user_email'] = $user['email'];
$_SESSION['user_role'] = $user['role'];
$_SESSION['is_2fa_verified'] = true;
$_SESSION['auth_date'] = $today;
$_SESSION['session_expires_at'] = strtotime('today 23:59:59');
// Verleng uitsluitend tot 23:59:59 van vandaag
issue_remember_token((int)$user['id']);
return $user;
} else {
clear_remember_cookie();
}
}
return null;
}
@@ -264,7 +230,7 @@ function require_auth(bool $json = false): array {
exit;
}
// 2. Controleer of gebruiker ingelogd én 2FA geverifieerd is
// 2. Controleer of gebruiker ingelogd én 2FA geverifieerd is voor vandaag
$user = get_logged_in_user();
if (!$user) {
if ($isAjax) {
@@ -273,7 +239,13 @@ function require_auth(bool $json = false): array {
echo json_encode(['success' => false, 'error' => 'Niet geauthenticeerd of sessie verlopen']);
exit;
}
header('Location: /auth/login.php');
$wasExpired = !empty($_SESSION['session_was_expired']);
unset($_SESSION['session_was_expired']);
if ($wasExpired) {
header('Location: /auth/login.php?expired=1');
} else {
header('Location: /auth/login.php');
}
exit;
}