This commit is contained in:
2026-09-15 12:30:58 +02:00
parent f992ca1ec9
commit 1358b6a7e9
5 changed files with 1140 additions and 531 deletions
+70 -6
View File
@@ -129,6 +129,12 @@ function fociz_log_audit(?int $userId, string $action, string $resourceType = ''
$statusNormalized,
$createdAt
]);
// Bewaartermijn audit logs: 3 maanden (90 dagen)
// Voer periodiek (1 op de 20 log-acties) een automatische opschoning uit
if (mt_rand(1, 20) === 1) {
$db->exec("DELETE FROM audit_logs WHERE created_at < NOW() - INTERVAL 90 DAY");
}
} catch (Throwable $e) {
// Audit log mag NOOIT de applicatiewerking breken
error_log("fociz_log_audit failure: " . $e->getMessage());
@@ -419,6 +425,36 @@ function fociz_get_device_name(?string $userAgent): string {
return "{$browser} op {$platform}";
}
/**
* Ruimt verlopen sessies op en beperkt het aantal sessies per gebruiker tot maximaal 20 (FIFO / oldest out)
*/
function fociz_cleanup_user_sessions(PDO $db, int $userId): void {
try {
// 1. Markeer sessies waarvan de geldigheidsduur is verstreken direct als inactief
$stmtExp = $db->prepare("UPDATE user_sessions SET is_active = 0 WHERE user_id = ? AND is_active = 1 AND expires_at < NOW()");
$stmtExp->execute([$userId]);
// 2. Beperk tot maximaal 20 sessies per gebruiker (FIFO: behoud de 20 meest recente, ruim oudere op)
$stmtLimit = $db->prepare("
SELECT id FROM user_sessions
WHERE user_id = ?
ORDER BY last_activity_at DESC, id DESC
LIMIT 20, 200
");
$stmtLimit->execute([$userId]);
$excessIds = $stmtLimit->fetchAll(PDO::FETCH_COLUMN);
if (!empty($excessIds)) {
$inClause = implode(',', array_map('intval', $excessIds));
$db->exec("DELETE FROM user_sessions WHERE id IN ($inClause)");
}
// 3. Verwijder inactieve/verlopen sessies ouder dan 7 dagen over de gehele tabel
$db->exec("DELETE FROM user_sessions WHERE is_active = 0 AND (expires_at < NOW() - INTERVAL 7 DAY OR last_activity_at < NOW() - INTERVAL 7 DAY)");
} catch (Throwable $e) {
error_log("fociz_cleanup_user_sessions error: " . $e->getMessage());
}
}
/**
* Registreert of vernieuwt een actieve sessie in user_sessions (SOC 2 CC6.1 / ISO 27001 A.9.4.2)
*/
@@ -447,8 +483,8 @@ function fociz_register_active_session(int $userId, string $sessionId, int $maxI
");
$stmt->execute([$userId, $hash, $ip, $ua, $deviceName, $expiresAt]);
// Verwijder oude verlopen sessies (> 7 dagen geleden verlopen)
$db->exec("DELETE FROM user_sessions WHERE expires_at < NOW() - INTERVAL 7 DAY");
// Ruim verlopen sessies op en handhaaf max 20 sessies
fociz_cleanup_user_sessions($db, $userId);
} catch (Throwable $e) {
error_log("fociz_register_active_session error: " . $e->getMessage());
}
@@ -505,7 +541,14 @@ function fociz_is_session_active(string $sessionId, int $userId): bool {
fociz_register_active_session($userId, $sessionId, $maxIdleSeconds);
return true;
}
return (!empty($row['is_active']));
if (empty($row['is_active'])) {
return false;
}
if (!empty($row['expires_at']) && strtotime($row['expires_at']) < time()) {
$db->prepare("UPDATE user_sessions SET is_active = 0 WHERE session_id_hash = ?")->execute([$hash]);
return false;
}
return true;
} catch (Throwable $e) {
return true; // Fallback op toegestaan bij databaseverstoring
}
@@ -567,7 +610,7 @@ function fociz_revoke_session_by_id(int $userId, int $sessionDbId): bool {
}
/**
* Haalt alle actieve sessies van een gebruiker op voor de profielpagina
* Haalt alle actieve sessies van een gebruiker op voor de profielpagina (maximaal 20, actieve expiratiedatum)
*/
function fociz_get_user_active_sessions(int $userId, string $currentSessionId): array {
try {
@@ -575,14 +618,35 @@ function fociz_get_user_active_sessions(int $userId, string $currentSessionId):
ensure_sessions_table($db);
$currentHash = hash('sha256', $currentSessionId);
// Voer direct opschoning uit: deactiveer verlopen sessies en handhaaf limiet van max 20
fociz_cleanup_user_sessions($db, $userId);
$stmt = $db->prepare("
SELECT id, ip_address, user_agent, device_name, last_activity_at, created_at, (session_id_hash = ?) AS is_current
FROM user_sessions
WHERE user_id = ? AND is_active = 1
WHERE user_id = ? AND is_active = 1 AND expires_at >= NOW()
ORDER BY is_current DESC, last_activity_at DESC
LIMIT 20
");
$stmt->execute([$currentHash, $userId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
$sessions = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
// Zorg dat het huidige apparaat altijd geregistreerd staat als actief
$hasCurrent = false;
foreach ($sessions as $s) {
if (!empty($s['is_current'])) {
$hasCurrent = true;
break;
}
}
if (!$hasCurrent && !empty($currentSessionId)) {
$maxIdleSeconds = function_exists('get_session_idle_timeout') ? get_session_idle_timeout() : 1800;
fociz_register_active_session($userId, $currentSessionId, $maxIdleSeconds);
$stmt->execute([$currentHash, $userId]);
$sessions = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
}
return $sessions;
} catch (Throwable $e) {
return [];
}