SERVICE_SESSION_TTL, 'path' => '/service/', 'secure' => !empty($_SERVER['HTTPS']), 'httponly' => true, 'samesite' => 'Strict', ]); session_name('AEGIS_SRV'); session_start(); } } /** * ---------- LOGIN ---------- */ define('BRUTE_FORCE_WINDOW', 180); define('BRUTE_FORCE_MAX_ATTEMPTS', 3); function auth_brute_force_check(string $login): bool { $pdo = getDB(); $stmt = $pdo->prepare("SELECT COUNT(*) FROM login_attempts WHERE (login = ? OR ip_address = ?) AND attempt_time > DATE_SUB(NOW(), INTERVAL ? SECOND)"); $stmt->execute([$login, $_SERVER['REMOTE_ADDR'] ?? '', BRUTE_FORCE_WINDOW]); return $stmt->fetchColumn() >= BRUTE_FORCE_MAX_ATTEMPTS; } function auth_record_attempt(string $login): void { $pdo = getDB(); $stmt = $pdo->prepare("INSERT INTO login_attempts (ip_address, login) VALUES (?, ?)"); $stmt->execute([$_SERVER['REMOTE_ADDR'] ?? '', $login]); } function auth_clear_attempts(string $login): void { $pdo = getDB(); $stmt = $pdo->prepare("DELETE FROM login_attempts WHERE login = ? OR ip_address = ?"); $stmt->execute([$login, $_SERVER['REMOTE_ADDR'] ?? '']); } function auth_login(string $login, string $password): array { $pdo = getDB(); if (auth_brute_force_check($login)) { return ['success' => false, 'error' => 'Превышен лимит попыток. Подождите 3 минуты.']; } $stmt = $pdo->prepare("SELECT id, login, password_hash, role, full_name, is_active FROM users WHERE login = ? LIMIT 1"); $stmt->execute([$login]); $user = $stmt->fetch(); if (!$user || !password_verify($password, $user['password_hash'])) { auth_record_attempt($login); return ['success' => false, 'error' => 'Неверный логин или пароль.']; } if (!$user['is_active']) { return ['success' => false, 'error' => 'Учётная запись заблокирована.']; } auth_clear_attempts($login); $_SESSION['user_id'] = (int)$user['id']; $_SESSION['user_login'] = $user['login']; $_SESSION['user_role'] = $user['role']; $_SESSION['user_name'] = $user['full_name']; $_SESSION['auth_time'] = time(); $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); auth_log($user['id'], 'login', 'Вход в систему'); return ['success' => true]; } /** * ---------- LOGOUT ---------- */ function auth_logout(): void { if (!empty($_SESSION['user_id'])) { auth_log($_SESSION['user_id'], 'logout', 'Выход из системы'); } $_SESSION = []; if (ini_get('session.use_cookies')) { setcookie(session_name(), '', time() - 42000, '/service/'); } session_destroy(); } /** * ---------- GUARD ---------- */ function auth_require(): array { auth_session_start(); $allowed_roles = func_get_args(); if (empty($_SESSION['user_id'])) { header('Location: /service/index.php?redirect=' . urlencode($_SERVER['REQUEST_URI'])); exit; } if (!empty($allowed_roles) && !in_array($_SESSION['user_role'], $allowed_roles, true)) { http_response_code(403); require __DIR__ . '/../pages/shared/403.php'; exit; } return $_SESSION; } function auth_has_role(string $role): bool { return isset($_SESSION['user_role']) && $_SESSION['user_role'] === $role; } /** * ---------- CSRF ---------- */ function auth_csrf_token(): string { return $_SESSION['csrf_token'] ?? ''; } function auth_csrf_verify(string $token): bool { return hash_equals($_SESSION['csrf_token'] ?? '', $token); } /** * ---------- AUDIT LOG ---------- */ function auth_log(?int $userId, string $action, string $details = ''): void { try { $pdo = getDB(); $stmt = $pdo->prepare("INSERT INTO audit_log (user_id, action, details, ip_address) VALUES (?, ?, ?, ?)"); $stmt->execute([$userId, $action, $details, $_SERVER['REMOTE_ADDR'] ?? '']); } catch (\Throwable $e) { // silent } }