From 8c291e0c8467e961571c67f9bb46efaad1b31bf3 Mon Sep 17 00:00:00 2001 From: hyzen Date: Wed, 29 Jul 2026 00:34:25 +0200 Subject: [PATCH] Fix: auto-logout sessions + another minor hardening --- admin.php | 1 + auth.php | 15 +++++++++++---- comments.php | 10 ++++++++++ full-setup.sh | 7 ++++++- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/admin.php b/admin.php index 306ede3..3dfc8be 100644 --- a/admin.php +++ b/admin.php @@ -218,6 +218,7 @@ function xmpp_delete_backup(string $username): void { // ── Session + admin check ── if (session_status() === PHP_SESSION_NONE) { + ini_set('session.gc_maxlifetime', (string) SESSION_TTL); session_name(SESSION_NAME); session_set_cookie_params([ 'lifetime' => 0, diff --git a/auth.php b/auth.php index 12ced83..5c67aae 100644 --- a/auth.php +++ b/auth.php @@ -37,7 +37,6 @@ define('PROSODY_HOST', $env['PROSODY_HOST'] ?? 'freedoms4.org'); define('SESSION_NAME', 'f4_session'); define('SESSION_SECURE', true); define('SESSION_SAMESITE', 'None'); -define('SESSION_TTL', 86400); // 24 hours define('OTP_FROM', 'no-reply@freedoms4.org'); define('OTP_TTL', 600); // 10 minutes @@ -79,6 +78,7 @@ function json_out(array $data, int $status = 200): never { function start_session(): void { if (session_status() === PHP_SESSION_NONE) { + ini_set('session.gc_maxlifetime', (string) SESSION_TTL); session_name(SESSION_NAME); session_set_cookie_params([ 'lifetime' => 0, @@ -600,17 +600,24 @@ if ($action === 'check_session') { json_out(['valid' => false]); } - // Verify user still exists in DB + // Verify user still exists in DB and isn't blocked try { $pdo = db_connect(); - $stmt = $pdo->prepare('SELECT 1 FROM users WHERE id = :id LIMIT 1'); + $stmt = $pdo->prepare('SELECT blocked FROM users WHERE id = :id LIMIT 1'); $stmt->execute([':id' => $_SESSION['user_id']]); - if (!$stmt->fetch()) { + $row = $stmt->fetch(); + if (!$row) { // User deleted — destroy session $_SESSION = []; session_destroy(); json_out(['valid' => false]); } + if ($row['blocked'] === true || $row['blocked'] === 't') { + // User blocked — destroy session + $_SESSION = []; + session_destroy(); + json_out(['valid' => false]); + } } catch (Exception $e) { // DB unavailable — don't force logout, just report invalid so frontend can retry json_out(['valid' => false, 'db_error' => true]); diff --git a/comments.php b/comments.php index 0a7321b..5e4b21d 100644 --- a/comments.php +++ b/comments.php @@ -84,6 +84,7 @@ function db_connect(): PDO { function start_session(): void { if (session_status() === PHP_SESSION_NONE) { + ini_set('session.gc_maxlifetime', (string) SESSION_TTL); session_name(SESSION_NAME); session_set_cookie_params([ 'lifetime' => 0, @@ -171,6 +172,15 @@ function send_notification(string $type, string $actor, string $body, string $po function logged_in_user(): ?array { if (empty($_SESSION['user_id']) || empty($_SESSION['username'])) return null; + + // Enforce same TTL as auth.php + $last_seen = $_SESSION['last_seen'] ?? 0; + if (time() - $last_seen > SESSION_TTL) { + $_SESSION = []; + session_destroy(); + return null; + } + // Verify the user still exists in the DB (handles deleted accounts / wiped DB) try { $pdo = db_connect(); diff --git a/full-setup.sh b/full-setup.sh index 43ef648..bec459c 100755 --- a/full-setup.sh +++ b/full-setup.sh @@ -21,12 +21,14 @@ DB_USER="" DB_PASS="" PROSODY_DB_USER="" PROSODY_DB_PASS="" # must match /etc/prosody/prosody.cfg.lua -DOMAIN="" +DOMAIN="backend.freedoms4.org" CERTBOT_EMAIL="" API_DIR="" ENV_FILE="" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" OTP_FROM="" # for example: no-reply@freedoms4.org +SESSION_GC_MAXLIFETIME=86400 # time to auto-logout a session in seconds + # Must run as root if [[ $EUID -ne 0 ]]; then @@ -365,6 +367,7 @@ fi mkdir -p "${API_DIR}" cp "${SCRIPT_DIR}/auth.php" "${API_DIR}/auth.php" +sed -i "/define('SESSION_SAMESITE',/a define('SESSION_TTL', ${SESSION_GC_MAXLIFETIME});" "${API_DIR}/auth.php" chown -R www-data:www-data "${API_DIR}" chmod 640 "${API_DIR}/auth.php" success "auth.php deployed." @@ -373,6 +376,7 @@ if [[ ! -f "${SCRIPT_DIR}/comments.php" ]]; then error "comments.php not found in ${SCRIPT_DIR}." fi cp "${SCRIPT_DIR}/comments.php" "${API_DIR}/comments.php" +sed -i "/define('SESSION_SAMESITE',/a define('SESSION_TTL', ${SESSION_GC_MAXLIFETIME});" "${API_DIR}/comments.php" chown www-data:www-data "${API_DIR}/comments.php" chmod 640 "${API_DIR}/comments.php" success "comments.php deployed." @@ -381,6 +385,7 @@ if [[ ! -f "${SCRIPT_DIR}/admin.php" ]]; then error "admin.php not found in ${SCRIPT_DIR}." fi cp "${SCRIPT_DIR}/admin.php" "${API_DIR}/admin.php" +sed -i "/define('SESSION_SAMESITE',/a define('SESSION_TTL', ${SESSION_GC_MAXLIFETIME});" "${API_DIR}/admin.php" chown www-data:www-data "${API_DIR}/admin.php" chmod 640 "${API_DIR}/admin.php" success "admin.php deployed."