!empty($_POST['session_ip_binding_enabled']), 'security.captcha.registration_enabled' => !empty($_POST['captcha_registration_enabled']), 'security.captcha.login_enabled' => !empty($_POST['captcha_login_enabled']), 'security.captcha.honeypot.enabled' => !empty($_POST['captcha_honeypot_enabled']), 'security.captcha.internal.enabled' => !empty($_POST['captcha_internal_enabled']), 'security.captcha.internal.difficulty' => $captchaDifficulty, 'security.captcha.internal.ttl_seconds' => max(60, min(1800, (int)($_POST['captcha_internal_ttl'] ?? 300))), 'security.captcha.internal.max_attempts' => max(1, min(10, (int)($_POST['captcha_internal_attempts'] ?? 3))), 'security.captcha.google.enabled' => $googleEnabled, 'security.captcha.google.version' => $captchaVersion, 'security.captcha.google.site_key' => $googleSiteKey, 'security.captcha.google.secret_key' => $googleSecretKey, 'security.captcha.google.v3_min_score' => max(0.0, min(1.0, (float)($_POST['captcha_google_v3_score'] ?? 0.5))), 'security.captcha.google.hostname' => trim((string)($_POST['captcha_google_hostname'] ?? '')), ]); return 'Spam and authentication protection settings saved.'; } if ($action === 'lock_user_session') { $userId = (int)($_POST['user_id'] ?? 0); $sessionId = trim((string)($_POST['session_id'] ?? '')); $db = getDB(); $cutoff = time() - ((int)getConfigVal('security.session_timeout_hours', 24) * 3600); $stmt = $db->prepare("SELECT id,user_id,ip FROM sessions WHERE id=? AND user_id=? AND last_active>?"); $stmt->execute([$sessionId, $userId, $cutoff]); $session = $stmt->fetch(); if (!$session) throw new RuntimeException('The selected session is no longer available.'); $db->beginTransaction(); try { $db->prepare("DELETE FROM sessions WHERE user_id=? AND id<>?")->execute([$userId, $sessionId]); $db->prepare("UPDATE users SET session_locked=1,session_lock_ip=?, session_lock_session_id=?,session_locked_at=? WHERE id=?") ->execute([$session['ip'], $sessionId, time(), $userId]); $db->commit(); } catch (Throwable $e) { if ($db->inTransaction()) $db->rollBack(); throw $e; } return 'User locked to the selected session and IP.'; } if ($action === 'unlock_user_session') { $userId = (int)($_POST['user_id'] ?? 0); $stmt = getDB()->prepare("UPDATE users SET session_locked=0,session_lock_ip=NULL, session_lock_session_id=NULL,session_locked_at=NULL WHERE id=?"); $stmt->execute([$userId]); if ($stmt->rowCount() < 1) throw new RuntimeException('User was not found or was already unlocked.'); return 'User session lock removed. Their next login will replace any existing session.'; } return null; } function renderAdminSpamTab(): void { $db = getDB(); $query = trim((string)($_GET['q'] ?? '')); $cutoff = time() - ((int)getConfigVal('security.session_timeout_hours', 24) * 3600); $where = ' WHERE s.last_active>?'; $params = [$cutoff]; if ($query !== '') { $where .= ' AND (u.username LIKE ? OR s.ip LIKE ?)'; $like = '%' . $query . '%'; $params[] = $like; $params[] = $like; } $sessionStmt = $db->prepare("SELECT s.*,u.username,u.color,u.session_locked, u.session_lock_ip,u.session_lock_session_id,u.session_locked_at FROM sessions s JOIN users u ON u.id=s.user_id $where ORDER BY s.last_active DESC LIMIT 500"); $sessionStmt->execute($params); $sessions = $sessionStmt->fetchAll(); $lockedStmt = $db->prepare("SELECT u.id,u.username,u.color,u.session_lock_ip, u.session_lock_session_id,u.session_locked_at,s.last_active FROM users u LEFT JOIN sessions s ON s.id=u.session_lock_session_id WHERE u.session_locked=1" . ($query !== '' ? " AND (u.username LIKE ? OR u.session_lock_ip LIKE ?)" : '') . " ORDER BY u.session_locked_at DESC LIMIT 500"); $lockedStmt->execute($query !== '' ? array_slice($params, 1) : []); $lockedUsers = $lockedStmt->fetchAll(); ?>
// session pinning, login replacement, honeypot, and CAPTCHA controls
| User | IP | Session | Last Active | Lock State | Actions |
|---|---|---|---|---|---|
| = esc($session['username']) ?> | = esc($session['ip']) ?> | = esc(substr((string)$session['id'], 0, 16)) ?>... | = ago((int)$session['last_active']) ?> | = $isPinned ? 'LOCKED HERE' : 'REPLACEABLE' ?> |
| User | Pinned IP | Pinned Session | Locked | Session Status | Actions |
|---|---|---|---|---|---|
| = esc($user['username']) ?> | = esc($user['session_lock_ip'] ?: '-') ?> | = $user['session_lock_session_id'] ? esc(substr((string)$user['session_lock_session_id'], 0, 16)) . '...' : '-' ?> | = $user['session_locked_at'] ? fmtTime((int)$user['session_locked_at']) : '-' ?> | = $hasActiveSession ? 'ACTIVE' : 'MISSING / EXPIRED' ?> |