feat(security): implement session management features including idle timeout, keepalive, and secure logout

This commit is contained in:
2026-09-11 15:47:09 +02:00
parent dc740fb94d
commit e012d6d21c
7 changed files with 515 additions and 5 deletions
+121
View File
@@ -238,6 +238,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
}
}
// 6. Sessie- & Beveiligingsinstellingen opslaan (ISO 27001 / NIST SP 800-63B)
elseif ($action === 'save_security_settings') {
$timeout = (int)($_POST['session_idle_timeout'] ?? 1800);
if (!in_array($timeout, [900, 1800, 3600, 7200], true)) {
$timeout = 1800;
}
set_system_setting('session_idle_timeout', $timeout);
fociz_log_audit($userId, 'SETTINGS_UPDATED', 'security_settings', null, ['session_idle_timeout' => $timeout], 'SUCCESS', $adminUser['email']);
$message = "Sessie- en beveiligingsinstellingen zijn succesvol opgeslagen.";
$messageType = 'success';
}
}
}
@@ -246,6 +260,7 @@ $appConfig = microsoft_get_app_config();
$companyJiraDomain = jira_get_company_domain();
$workdaySettings = get_workday_settings();
$aiSettings = get_ai_settings();
$sessionIdleTimeout = get_session_idle_timeout();
// Redirect URI
$redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php';
@@ -1128,6 +1143,97 @@ require_once __DIR__ . '/includes/header.php';
</form>
</div>
</div>
<!-- 6. Sessie Inactiviteit & Beveiligingsbeleid (Release v1.6) -->
<div class="fociz-card p-6 sm:p-7 space-y-6">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3 pb-4 border-b border-slate-100 dark:border-slate-800/80">
<div class="flex items-center space-x-3.5">
<div class="w-12 h-12 rounded-xl bg-amber-50 dark:bg-amber-950/40 border border-amber-200 dark:border-amber-800/60 flex items-center justify-center text-amber-600 dark:text-amber-400 font-bold shadow-xs">
<i data-lucide="shield-alert" class="w-6 h-6"></i>
</div>
<div>
<h2 class="text-lg font-bold text-slate-900 dark:text-white tracking-tight">Sessie Inactiviteit & Beveiligingsbeleid</h2>
<p class="text-xs text-slate-500 dark:text-slate-400">Automatische afmelding bij inactiviteit conform NIST SP 800-63B, OWASP & ISO 27001</p>
</div>
</div>
<span class="px-2.5 py-1 rounded-full bg-emerald-50 dark:bg-emerald-950/40 text-emerald-700 dark:text-emerald-400 border border-emerald-200 dark:border-emerald-800/60 text-xs font-bold self-start sm:self-auto flex items-center space-x-1.5">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse"></span>
<span>SOC 2 / ISO 27001 Compliant</span>
</span>
</div>
<form method="POST" action="/admin.php" class="space-y-5">
<input type="hidden" name="action" value="save_security_settings">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars(get_csrf_token()) ?>">
<div>
<label class="block text-xs font-semibold text-slate-700 dark:text-slate-300 mb-2">
Inactiviteitstimeout (Session Idle Expiration)
</label>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<!-- 15 minuten -->
<label class="relative flex items-start p-3.5 rounded-2xl border cursor-pointer transition <?= $sessionIdleTimeout === 900 ? 'bg-amber-50/60 dark:bg-amber-950/20 border-amber-400 dark:border-amber-600 ring-1 ring-amber-400/40' : 'bg-slate-50 dark:bg-[#0c0f17] border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700' ?>">
<input type="radio" name="session_idle_timeout" value="900" <?= $sessionIdleTimeout === 900 ? 'checked' : '' ?> class="mt-0.5 text-amber-600 focus:ring-amber-500 border-slate-300 dark:border-slate-700">
<div class="ml-3">
<span class="block text-xs font-bold text-slate-900 dark:text-white">15 minuten</span>
<span class="block text-[11px] text-slate-500 dark:text-slate-400 mt-0.5">Strikt — ISO 27001 & Overheid BIO richtlijn</span>
</div>
</label>
<!-- 30 minuten (aanbevolen) -->
<label class="relative flex items-start p-3.5 rounded-2xl border cursor-pointer transition <?= $sessionIdleTimeout === 1800 ? 'bg-indigo-50/60 dark:bg-indigo-950/20 border-indigo-400 dark:border-indigo-600 ring-1 ring-indigo-400/40' : 'bg-slate-50 dark:bg-[#0c0f17] border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700' ?>">
<input type="radio" name="session_idle_timeout" value="1800" <?= $sessionIdleTimeout === 1800 ? 'checked' : '' ?> class="mt-0.5 text-indigo-600 focus:ring-indigo-500 border-slate-300 dark:border-slate-700">
<div class="ml-3">
<div class="flex items-center space-x-1.5">
<span class="text-xs font-bold text-slate-900 dark:text-white">30 minuten</span>
<span class="px-1.5 py-0.2 rounded text-[9px] font-extrabold bg-indigo-100 dark:bg-indigo-900/50 text-indigo-700 dark:text-indigo-300">Aanbevolen</span>
</div>
<span class="block text-[11px] text-slate-500 dark:text-slate-400 mt-0.5">NIST SP 800-63B & OWASP ASVS v4.0 standaard</span>
</div>
</label>
<!-- 60 minuten -->
<label class="relative flex items-start p-3.5 rounded-2xl border cursor-pointer transition <?= $sessionIdleTimeout === 3600 ? 'bg-indigo-50/60 dark:bg-indigo-950/20 border-indigo-400 dark:border-indigo-600 ring-1 ring-indigo-400/40' : 'bg-slate-50 dark:bg-[#0c0f17] border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700' ?>">
<input type="radio" name="session_idle_timeout" value="3600" <?= $sessionIdleTimeout === 3600 ? 'checked' : '' ?> class="mt-0.5 text-indigo-600 focus:ring-indigo-500 border-slate-300 dark:border-slate-700">
<div class="ml-3">
<span class="block text-xs font-bold text-slate-900 dark:text-white">60 minuten</span>
<span class="block text-[11px] text-slate-500 dark:text-slate-400 mt-0.5">Kantoorstandaard — Balans tussen gebruiksgemak & veiligheid</span>
</div>
</label>
<!-- 120 minuten -->
<label class="relative flex items-start p-3.5 rounded-2xl border cursor-pointer transition <?= $sessionIdleTimeout === 7200 ? 'bg-indigo-50/60 dark:bg-indigo-950/20 border-indigo-400 dark:border-indigo-600 ring-1 ring-indigo-400/40' : 'bg-slate-50 dark:bg-[#0c0f17] border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700' ?>">
<input type="radio" name="session_idle_timeout" value="7200" <?= $sessionIdleTimeout === 7200 ? 'checked' : '' ?> class="mt-0.5 text-indigo-600 focus:ring-indigo-500 border-slate-300 dark:border-slate-700">
<div class="ml-3">
<span class="block text-xs font-bold text-slate-900 dark:text-white">120 minuten (2 uur)</span>
<span class="block text-[11px] text-slate-500 dark:text-slate-400 mt-0.5">Flexibel — Minimale onderbreking bij geconcentreerd werk</span>
</div>
</label>
</div>
</div>
<!-- Beveiligingsmaatregelen infopanel -->
<div class="p-4 rounded-2xl bg-slate-50 dark:bg-[#0c0f17] border border-slate-200 dark:border-slate-800/80 space-y-2 text-xs">
<div class="font-bold text-slate-800 dark:text-slate-200 flex items-center space-x-1.5">
<i data-lucide="info" class="w-3.5 h-3.5 text-indigo-500"></i>
<span>Actieve beveiligingsmaatregelen:</span>
</div>
<ul class="space-y-1.5 text-[11px] text-slate-600 dark:text-slate-400 list-disc list-inside">
<li><strong>Interactieve waarschuwing:</strong> 2 minuten (120 sec) voor time-out verschijnt een aftelmodal. Actieve gebruikers kunnen met 1 klik ingelogd blijven.</li>
<li><strong>Periodieke Sessierotatie:</strong> Sessie-ID's worden conform OWASP ASVS v4.0 automatisch elke 30 minuten geroteerd tegen session hijacking.</li>
<li><strong>Absolute daglimiet:</strong> Maximaal 10 uur aaneengesloten sessie per kalenderdag; om middernacht vervalt elke sessie automatisch.</li>
<li><strong>Tabblad-synchronisatie:</strong> Activiteit in één browsertabblad verlengt automatisch alle open Fociz-tabbladen.</li>
</ul>
</div>
<div class="flex justify-end pt-2">
<button type="submit" class="px-5 py-2.5 rounded-xl bg-indigo-600 hover:bg-indigo-500 text-white text-xs font-bold transition shadow-xs cursor-pointer">
Sessie-instellingen Opslaan
</button>
</div>
</form>
</div>
</div>
<!-- Rechterkolom: Systeem & Team Monitor (4 cols) -->
@@ -1193,6 +1299,21 @@ require_once __DIR__ . '/includes/header.php';
</span>
</div>
<div class="flex items-center justify-between p-2.5 rounded-xl bg-slate-50 dark:bg-[#0c0f17] border border-slate-200 dark:border-slate-800">
<span>Sessie Idle Timeout</span>
<span class="text-indigo-600 dark:text-indigo-400 font-bold"><?= (int)($sessionIdleTimeout / 60) ?> min</span>
</div>
<div class="flex items-center justify-between p-2.5 rounded-xl bg-slate-50 dark:bg-[#0c0f17] border border-slate-200 dark:border-slate-800">
<span>Sessie ID Rotatie</span>
<span class="text-teal-600 dark:text-teal-400 font-bold">Elke 30 min</span>
</div>
<div class="flex items-center justify-between p-2.5 rounded-xl bg-slate-50 dark:bg-[#0c0f17] border border-slate-200 dark:border-slate-800">
<span>Wachtwoordbeleid</span>
<span class="text-teal-600 dark:text-teal-400 font-bold">NIST 12+ tekens</span>
</div>
<div class="flex items-center justify-between p-2.5 rounded-xl bg-slate-50 dark:bg-[#0c0f17] border border-slate-200 dark:border-slate-800">
<span>Database Versie</span>
<span class="text-slate-500 dark:text-slate-400 font-mono text-[11px]">MariaDB 10.x</span>