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:
@@ -254,6 +254,10 @@
|
|||||||
isFetchingEvents = true;
|
isFetchingEvents = true;
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/notifications.php?action=get_events', { cache: 'no-store' });
|
const res = await fetch('/api/notifications.php?action=get_events', { cache: 'no-store' });
|
||||||
|
if (res.status === 401) {
|
||||||
|
window.location.href = '/auth/login.php?expired=1';
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data && data.success) {
|
if (data && data.success) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ if (empty($_SESSION['user_id']) && $stateUserId) {
|
|||||||
$_SESSION['is_2fa_verified'] = true;
|
$_SESSION['is_2fa_verified'] = true;
|
||||||
$_SESSION['auth_date'] = date('Y-m-d');
|
$_SESSION['auth_date'] = date('Y-m-d');
|
||||||
$_SESSION['session_expires_at'] = strtotime('today 23:59:59');
|
$_SESSION['session_expires_at'] = strtotime('today 23:59:59');
|
||||||
issue_remember_token((int)$recoveredUser['id']);
|
clear_remember_cookie();
|
||||||
}
|
}
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
// Foutafhandeling
|
// Foutafhandeling
|
||||||
|
|||||||
+1
-1
@@ -83,7 +83,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$_SESSION['is_2fa_verified'] = true;
|
$_SESSION['is_2fa_verified'] = true;
|
||||||
$_SESSION['auth_date'] = date('Y-m-d');
|
$_SESSION['auth_date'] = date('Y-m-d');
|
||||||
$_SESSION['session_expires_at'] = strtotime('today 23:59:59');
|
$_SESSION['session_expires_at'] = strtotime('today 23:59:59');
|
||||||
issue_remember_token($newUserId);
|
clear_remember_cookie();
|
||||||
unset($_SESSION['invite_totp_secret']);
|
unset($_SESSION['invite_totp_secret']);
|
||||||
|
|
||||||
header('Location: /planner.php');
|
header('Location: /planner.php');
|
||||||
|
|||||||
+2
-2
@@ -108,8 +108,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$_SESSION['auth_date'] = date('Y-m-d');
|
$_SESSION['auth_date'] = date('Y-m-d');
|
||||||
$_SESSION['session_expires_at'] = strtotime('today 23:59:59');
|
$_SESSION['session_expires_at'] = strtotime('today 23:59:59');
|
||||||
|
|
||||||
// Activeer dagspecifieke remember token (verloopt vannacht om 23:59:59)
|
// Zorg dat er geen persistente remember cookies worden gebruikt
|
||||||
issue_remember_token((int)$user['id']);
|
clear_remember_cookie();
|
||||||
|
|
||||||
unset($_SESSION['pending_2fa_user_id'], $_SESSION['pending_2fa_user_email'], $_SESSION['pending_2fa_user_name']);
|
unset($_SESSION['pending_2fa_user_id'], $_SESSION['pending_2fa_user_email'], $_SESSION['pending_2fa_user_name']);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -70,7 +70,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$_SESSION['is_2fa_verified'] = true;
|
$_SESSION['is_2fa_verified'] = true;
|
||||||
$_SESSION['auth_date'] = date('Y-m-d');
|
$_SESSION['auth_date'] = date('Y-m-d');
|
||||||
$_SESSION['session_expires_at'] = strtotime('today 23:59:59');
|
$_SESSION['session_expires_at'] = strtotime('today 23:59:59');
|
||||||
issue_remember_token($adminId);
|
clear_remember_cookie();
|
||||||
unset($_SESSION['setup_totp_secret']);
|
unset($_SESSION['setup_totp_secret']);
|
||||||
|
|
||||||
$success = true;
|
$success = true;
|
||||||
|
|||||||
+42
-70
@@ -76,6 +76,9 @@ function ensure_remember_token_columns(PDO $db): void {
|
|||||||
$result = $db->query("SHOW COLUMNS FROM `users` LIKE 'remember_token'");
|
$result = $db->query("SHOW COLUMNS FROM `users` LIKE 'remember_token'");
|
||||||
if (!$result || $result->rowCount() === 0) {
|
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`");
|
$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) {
|
} catch (Throwable $e) {
|
||||||
// Stil negeren indien al aanwezig of tijdens migraties
|
// 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.
|
* Verouderde functie voor persistent login.
|
||||||
* Verloopt altijd dezelfde nacht om 23:59:59 zodat sessies niet meerdere dagen blijven staan.
|
* 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 {
|
function issue_remember_token(int $userId): string {
|
||||||
$rawToken = bin2hex(random_bytes(32));
|
clear_remember_cookie();
|
||||||
$tokenHash = hash('sha256', $rawToken);
|
try {
|
||||||
$midnightTs = strtotime('today 23:59:59');
|
$db = get_db_connection();
|
||||||
$expiresAt = date('Y-m-d 23:59:59', $midnightTs);
|
ensure_remember_token_columns($db);
|
||||||
|
$stmt = $db->prepare("UPDATE users SET remember_token = NULL, remember_token_expires_at = NULL WHERE id = ?");
|
||||||
$db = get_db_connection();
|
$stmt->execute([$userId]);
|
||||||
ensure_remember_token_columns($db);
|
} catch (Throwable $e) {}
|
||||||
|
return '';
|
||||||
$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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verwijdert de persistente remember cookie
|
* Verwijdert de persistente remember cookie uit de browser
|
||||||
*/
|
*/
|
||||||
function clear_remember_cookie(): void {
|
function clear_remember_cookie(): void {
|
||||||
$isSecure = is_https_request();
|
$isSecure = is_https_request();
|
||||||
@@ -158,11 +149,15 @@ function clear_remember_cookie(): void {
|
|||||||
'httponly' => true,
|
'httponly' => true,
|
||||||
'samesite' => 'Lax'
|
'samesite' => 'Lax'
|
||||||
]);
|
]);
|
||||||
|
if (isset($_COOKIE['fociz_remember'])) {
|
||||||
|
unset($_COOKIE['fociz_remember']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Haalt de huidige ingelogde gebruiker op uit de database.
|
* 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.
|
* 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 {
|
function get_logged_in_user(): ?array {
|
||||||
start_secure_session();
|
start_secure_session();
|
||||||
@@ -170,18 +165,22 @@ function get_logged_in_user(): ?array {
|
|||||||
$today = date('Y-m-d');
|
$today = date('Y-m-d');
|
||||||
$now = time();
|
$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
|
// 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) {
|
if (!empty($_SESSION['auth_date']) && $_SESSION['auth_date'] !== $today) {
|
||||||
$_SESSION = [];
|
$isExpired = true;
|
||||||
clear_remember_cookie();
|
|
||||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
|
||||||
@session_regenerate_id(true);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($_SESSION['session_expires_at']) && $now > (int)$_SESSION['session_expires_at']) {
|
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();
|
clear_remember_cookie();
|
||||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||||
@session_regenerate_id(true);
|
@session_regenerate_id(true);
|
||||||
@@ -189,11 +188,10 @@ function get_logged_in_user(): ?array {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$db = get_db_connection();
|
// Controleer actieve PHP sessie (uitsluitend de sessie van vandaag is geldig)
|
||||||
ensure_remember_token_columns($db);
|
|
||||||
|
|
||||||
// 1. Controleer actieve PHP sessie
|
|
||||||
if (!empty($_SESSION['user_id']) && !empty($_SESSION['is_2fa_verified'])) {
|
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 = $db->prepare("SELECT id, name, email, role, two_factor_enabled, created_at FROM users WHERE id = ?");
|
||||||
$stmt->execute([$_SESSION['user_id']]);
|
$stmt->execute([$_SESSION['user_id']]);
|
||||||
$user = $stmt->fetch();
|
$user = $stmt->fetch();
|
||||||
@@ -207,38 +205,6 @@ function get_logged_in_user(): ?array {
|
|||||||
clear_remember_cookie();
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,7 +230,7 @@ function require_auth(bool $json = false): array {
|
|||||||
exit;
|
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();
|
$user = get_logged_in_user();
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
if ($isAjax) {
|
if ($isAjax) {
|
||||||
@@ -273,7 +239,13 @@ function require_auth(bool $json = false): array {
|
|||||||
echo json_encode(['success' => false, 'error' => 'Niet geauthenticeerd of sessie verlopen']);
|
echo json_encode(['success' => false, 'error' => 'Niet geauthenticeerd of sessie verlopen']);
|
||||||
exit;
|
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;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-1
@@ -57,10 +57,20 @@ $currentUserTheme = !empty($currentUser['id']) ? get_user_theme((int)$currentUse
|
|||||||
</script>
|
</script>
|
||||||
<!-- Lucide Icons -->
|
<!-- Lucide Icons -->
|
||||||
<script src="https://unpkg.com/lucide@latest"></script>
|
<script src="https://unpkg.com/lucide@latest"></script>
|
||||||
<!-- Dagsessie Expiratie Watchdog: reset iedere nacht om 23:59:59 -->
|
<!-- Dagsessie Expiratie Watchdog: reset iedere nacht om 23:59:59 en dwingt dagelijks opnieuw inloggen af -->
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
try {
|
try {
|
||||||
|
const sessionDate = "<?= date('Y-m-d') ?>";
|
||||||
|
const checkMidnight = function() {
|
||||||
|
const now = new Date();
|
||||||
|
const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
||||||
|
const todayStr = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0');
|
||||||
|
if (todayStr !== sessionDate || now.getTime() > midnight.getTime()) {
|
||||||
|
window.location.href = '/auth/login.php?expired=1';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
||||||
const msUntilMidnight = midnight.getTime() - now.getTime();
|
const msUntilMidnight = midnight.getTime() - now.getTime();
|
||||||
@@ -69,6 +79,15 @@ $currentUserTheme = !empty($currentUser['id']) ? get_user_theme((int)$currentUse
|
|||||||
window.location.href = '/auth/login.php?expired=1';
|
window.location.href = '/auth/login.php?expired=1';
|
||||||
}, msUntilMidnight);
|
}, msUntilMidnight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Controleer direct zodra de gebruiker de browser weer activeert (bij bijv. standby / slaapstand overnacht)
|
||||||
|
document.addEventListener('visibilitychange', function() {
|
||||||
|
if (document.visibilityState === 'visible') {
|
||||||
|
checkMidnight();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.addEventListener('focus', checkMidnight);
|
||||||
|
setInterval(checkMidnight, 30000);
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user