0, 'path' => '/', 'domain' => '', 'secure' => $secure, 'httponly' => true, 'samesite' => 'Strict', ]); ini_set('session.use_strict_mode', '1'); ini_set('session.cookie_httponly', '1'); session_name('torpanel'); session_start(); const APP_CONF_DIR = '/etc/torpanel'; const APP_CONF_FILE = APP_CONF_DIR . '/app.json'; const STATE_DIR = '/var/lib/torpanel'; const LOGIN_THROTTLE_FILE = STATE_DIR . '/login_throttle.json'; const SESSION_IDLE_TTL = 60 * 60 * 12; if (!empty($_SESSION['last']) && (time() - (int)$_SESSION['last']) > SESSION_IDLE_TTL) { $_SESSION = []; if (ini_get('session.use_cookies')) { $p = session_get_cookie_params(); setcookie(session_name(), '', time() - 42000, $p['path'], $p['domain'], $p['secure'] ?? false, $p['httponly'] ?? true); } session_destroy(); session_start(); } $_SESSION['last'] = time(); function app_is_installed(): bool { return is_file(APP_CONF_FILE); } function app_config(): array { if (!app_is_installed()) return []; $raw = @file_get_contents(APP_CONF_FILE); return $raw ? (json_decode($raw, true) ?: []) : []; } function app_save_config(array $cfg): void { if (!is_dir(APP_CONF_DIR)) mkdir(APP_CONF_DIR, 0770, true); $tmp = APP_CONF_FILE . '.tmp'; file_put_contents($tmp, json_encode($cfg, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); rename($tmp, APP_CONF_FILE); @chmod(APP_CONF_FILE, 0640); } function auth_login(string $u, string $p): bool { $cfg = app_config(); if (!isset($cfg['admin_user'], $cfg['admin_pass'])) return false; if (!hash_equals($cfg['admin_user'], $u)) return false; if (!password_verify($p, $cfg['admin_pass'])) return false; session_regenerate_id(true); $_SESSION['uid'] = $u; $_SESSION['ua'] = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 160); $_SESSION['last'] = time(); return true; } function auth_logout(): void { $_SESSION = []; if (ini_get('session.use_cookies')) { $p = session_get_cookie_params(); setcookie(session_name(), '', time() - 42000, $p['path'], $p['domain'], $p['secure'] ?? false, $p['httponly'] ?? true); } session_destroy(); } function auth_require(): void { if (!app_is_installed()) { header('Location: /setup.php'); exit; } if (empty($_SESSION['uid'])) { header('Location: /login.php'); exit; } } function throttle_state(): array { if (!is_dir(STATE_DIR)) @mkdir(STATE_DIR, 0775, true); $raw = @file_get_contents(LOGIN_THROTTLE_FILE); return $raw ? (json_decode($raw, true) ?: []) : []; } function throttle_save(array $st): void { $tmp = LOGIN_THROTTLE_FILE . '.tmp'; file_put_contents($tmp, json_encode($st)); @chmod($tmp, 0660); rename($tmp, LOGIN_THROTTLE_FILE); } function throttle_check(string $ip): int { $st = throttle_state(); $now = time(); $key = $ip ?: 'unknown'; $rec = $st[$key] ?? ['fails'=>0, 'until'=>0]; if ($rec['until'] > $now) return $rec['until'] - $now; return 0; } function throttle_record(string $ip, bool $ok): void { $st = throttle_state(); $now = time(); $key = $ip ?: 'unknown'; $rec = $st[$key] ?? ['fails'=>0, 'until'=>0]; if ($ok) { $rec = ['fails'=>0, 'until'=>0]; } else { $rec['fails'] = min(10, ($rec['fails'] ?? 0) + 1); $wait = min(300, 2 ** $rec['fails']); $rec['until'] = $now + $wait; } $st[$key] = $rec; throttle_save($st); }