Аутентификация и сеансы¶
XC_VM поддерживает три контекста аутентификации - администратора, реселлера и игрока - каждый с изолированными сеансовыми ключами, отдельными потоками входа в систему и независимой логикой проверки. В этом документе описан полный жизненный цикл аутентификации от входа в систему до проверки сеанса и обеспечения безопасности.
Обзор процесса входа в систему¶
Все три контекста следуют схожему шаблону высокого уровня, с зависящими от контекста различиями в проверке и хранении сеанса.
Взаимодействие администратора и реселлера¶
POST request with credentials
-> BruteforceGuard checks (flood / brute-force)
-> Optional reCAPTCHA verification
-> Credential lookup via UserRepository::getAuthUserByCredentials()
-> Access code / group validation
-> Permission check (is_admin or is_reseller)
-> User status check (enabled/disabled)
-> Password re-hash + session write + login log
Поток игроков¶
POST request with credentials
-> UserRepository::getUserInfo() lookup
-> Line type checks (reject E2, MAG, Stalker)
-> Expiration date check
-> admin_enabled / enabled status checks
-> IP allowlist, country restriction, user agent, ISP checks
-> Session write + redirect
-> BruteforceGuard::checkFlood() on any failure
Аутентификатор¶
Файл: src/Core/Auth/Authenticator.php
Authenticator::login(array $data, bool $bypassRecaptcha = false): array¶
Способ входа в систему администратора. Шаги по порядку:
- Проверка повторной проверки (если параметр
recaptcha_enableвключен и не пропущен). - Поиск учетных данных с помощью
UserRepository::getAuthUserByCredentials(). - Проверка группы кодов доступа - имя пользователя
member_group_idдолжно входить в разрешенные группы текущего кода доступа, в противном случае кодов доступа не должно существовать. - Permission check --
is_adminmust be true for the user's group. - Проверка состояния -
$rUserInfo['status'] == 1(включено). - В случае успеха: повторно хэширует пароль, обновляет
last_loginиipв базе данных, записывает сеансовые ключи, регистрирует логин.
Значения сеанса, записанные в зависимости от успеха:
$_SESSION['hash'] = $rUserInfo['id']; // User ID
$_SESSION['ip'] = $rIP; // Client IP at login
$_SESSION['code'] = AuthRepository::getCurrentCode(); // Current access code
$_SESSION['verify'] = md5($rUserInfo['username'] . '||' . $rCrypt); // Verification hash
Authenticator::resellerLogin(array $data): array¶
Метод входа в систему торгового посредника. Структура идентична login() с этими отличиями:
- Повторная проверка всегда проверяется, когда она включена (параметр обхода отсутствует).
- Для проверки разрешений требуется
is_resellerвместоis_admin. - Возвращает
STATUS_NOT_RESELLER, если у пользователя нет разрешения торгового посредника. - Журналы входа в систему записываются с типом
RESELLERвместоADMIN.
Значения сеанса, записанные в зависимости от успеха:
$_SESSION['reseller'] = $rUserInfo['id']; // User ID
$_SESSION['rip'] = $rIP; // Client IP at login
$_SESSION['rcode'] = AuthRepository::getCurrentCode(); // Current access code
$_SESSION['rverify'] = md5($rUserInfo['username'] . '||' . $rCrypt); // Verification hash
Константы статуса входа в систему¶
Определено в src/bootstrap.php через XC_Bootstrap::defineStatusConstants():
| Постоянный | Ценность | Значение |
|---|---|---|
STATUS_FAILURE |
0 | Общий сбой (неверные учетные данные или универсальная ошибка) |
STATUS_SUCCESS |
1 | Вход в систему был успешным |
STATUS_DISABLED |
5 | Учетная запись отключена |
STATUS_NOT_ADMIN |
6 | У пользователя нет прав администратора |
STATUS_INVALID_CAPTCHA |
12 | Не удалось выполнить проверку reCAPTCHA |
STATUS_INVALID_CODE |
13 | Несоответствие кода доступа/ группы |
STATUS_NOT_RESELLER |
35 | У пользователя нет разрешения торгового посредника |
Хэширование паролей¶
Использует crypt() с SHA-512 ($6$). Формат salt равен $6$rounds=20000$<salt>$, где <salt> - это 16 шестнадцатеричных символов, полученных из openssl_random_pseudo_bytes(16). Пароли повторно хэшируются при каждом успешном входе в систему, что приводит к замене значения salt.
Проверяет пароль в виде открытого текста на соответствие сохраненному хэшу, используя crypt($password, $storedHash), с возможностью сравнения по времени с помощью hash_equals(). Сохраненный хэш содержит алгоритм, раунды и соль, поэтому crypt() воспроизводит правильный хэш для сравнения.
Аутентификация игрока¶
Файл: src/Public/Controllers/Player/PlayerLoginController.php
Процедура входа игрока в систему принципиально отличается от процедуры администратора/реселлера. Она аутентифицирует "линии" конечного пользователя (подписки на IPTV), а не операторов панели.
Процесс входа в систему¶
PlayerLoginController::processLogin() выполняет эти проверки в порядке:
- Поиск учетных данных --
UserRepository::getUserInfo()(отличается отgetAuthUserByCredentials, используемого администратором/реселлером). - Отклонение типа линии -- Линии E2, MAG и Stalker отклоняются с определенными кодами ошибок.
- Проверка истечения срока годности --
exp_dateдолжно быть равно null или в будущем. - Проверка с поддержкой администратора --
admin_enabled == 0возвращаетCLIENT_BANNED. - Проверка, включенная пользователем --
enabled == 0возвращаетCLIENT_DISABLED. - Список разрешенных IP-адресов -- Если для пользователя задано значение
allowed_ips, IP-адрес клиента должен совпадать (решается с помощьюgethostbyname). - Ограничение по стране - Два режима:
- Для каждого пользователя: если задано значение
forced_country, а неALL, страна GeoIP должна совпадать. - Глобальный: если нет переопределения для каждого пользователя, устанавливается глобальный параметр
allow_countries(если только он не содержитALL). - Проверка агента пользователя -- Если для пользователя задано значение
allowed_ua, то пользовательский агент HTTP должен соответствовать. - флаг Проверка интернет-провайдера --
isp_violateотклоняет соединение. - Проверка сервера интернет-провайдера -- Если значение
isp_is_serverравно true и пользователь не является рестримером, соединение будет отклонено.
Каждый сбой запускает BruteforceGuard::checkFlood() перед возвратом кода ошибки.
Коды ошибок проигрывателя¶
| Постоянный | Ценность | Значение |
|---|---|---|
CLIENT_INVALID |
0 | Неверное имя пользователя или пароль |
CLIENT_IS_E2 |
1 | Линии Enigma запрещены |
CLIENT_IS_MAG |
2 | МАГНИТНЫЕ линии не допускаются |
CLIENT_IS_STALKER |
3 | Линии преследования запрещены |
CLIENT_EXPIRED |
4 | Срок действия строки истек |
CLIENT_BANNED |
5 | Строка заблокирована (admin_enabled = 0) |
CLIENT_DISABLED |
6 | Линия отключена (включено = 0) |
CLIENT_DISALLOWED |
7 | Не удалось выполнить проверку IP/страны/UA/провайдера |
Ключи к сеансу игрока¶
$_SESSION['phash'] = $rUserInfo['id'];
$_SESSION['pverify'] = md5($rUserInfo['username'] . '||' . $rUserInfo['password']);
В контексте player хранятся только два сеансовых ключа. В отличие от ключей администратора и реселлера, здесь нет ключей activity, ip или code. Это означает, что в сеансе player нет таймаута бездействия и не обнаруживается изменение IP-адреса на уровне сеанса.
Проверка сеанса при загрузке страницы¶
После первоначального входа в систему при каждой загрузке страницы, прошедшей проверку подлинности, выполняется повторная проверка сеанса. Это происходит в файлах начальной загрузки, а не в SessionManager.
Проверка сеанса администратора¶
Файл: src/Public/Views/admin/functions.php
Если установлено значение $_SESSION['hash'], при каждой загрузке страницы выполняются следующие проверки:
- Поиск пользователя --
UserRepository::getRegisteredUserById($_SESSION['hash']). Если пользователь больше не существует, сеанс завершается. - Проверка прав доступа --
AuthRepository::getPermissions()должен возвращать допустимый набор сis_admin == true. - Проверка IP-адреса - Сравнивает текущий IP-адрес с
$_SESSION['ip']: - Если параметр
ip_subnet_matchвключен: сравниваются только первые три октета (например,192.168.1.*соответствует192.168.1.*). - Если параметр
ip_subnet_matchотключен: требуется точное совпадение IP-адресов. - Если IP-адрес не совпадает и включена настройка
ip_logout, сеанс завершается. - Если IP-адрес не совпадает и
ip_logoutотключен,$_SESSION['ip']автоматически обновляется до нового IP-адреса. - Проверить проверку хэша Verify --
$_SESSION['verify']должно быть равноmd5($rUserInfo['username'] . '||' . $rUserInfo['password']). Это гарантирует, что сеанс будет аннулирован в случае изменения пароля.
Если какая-либо проверка завершается неудачей, сеанс очищается с помощью SessionManager::clearContext('admin'), и пользователь перенаправляется на индексную страницу.
Проверка сеанса работы с реселлером¶
Файл: src/Infrastructure/Bootstrap/reseller_functions.php
Логика идентична проверке администратора, но используются сеансовые ключи реселлера:
- Проверяет
$_SESSION['reseller']на наличие идентификатора пользователя. - Использует
$_SESSION['rip']для сравнения IP-адресов. - Использует
$_SESSION['rverify']для проверки хэша. - Проверяет разрешение
is_resellerвместоis_admin.
Соответствие IP-подсети и поведение при выходе из системы по IP-адресу такое же, как у администратора.
Тайм-аут сеанса администрирования¶
Файл: src/Public/Views/admin/session.php
Для сеансов администрирования выполняется отдельная проверка тайм-аута сеанса. Если заданы значения $_SESSION['hash'] и $_SESSION['last_activity'], а с момента last_activity прошло более 60 минут, ключи сеанса (hash, ip, code, verify, last_activity) не заданы. При каждом действительном запросе обновляется $_SESSION['last_activity'], и сеанс закрывается для записи.
Проверка сеанса игрока¶
Контекст проигрывателя не выполняет проверку IP-адреса, соответствие подсети или тайм-аут активности на уровне сеанса. Сохраняются только phash и pverify, и проверка выполняется на прикладном уровне для повторной проверки этих значений по базе данных.
Менеджер сеанса¶
Файл: src/Core/Auth/SessionManager.php
Унифицированный сеансовый API, который абстрагирует различные имена сеансовых ключей в разных контекстах. Предназначен для замены устаревших файлов admin/session.php и reseller/session.php.
Контекстная ключевая карта¶
| Логический ключ | Ключ администратора $_SESSION |
Ключ реселлера $_SESSION |
Ключ игрока $_SESSION |
|---|---|---|---|
auth |
hash |
reseller |
phash |
activity |
last_activity |
rlast_activity |
-- |
ip |
ip |
rip |
-- |
code |
code |
rcode |
-- |
verify |
verify |
rverify |
pverify |
Контекст игрока намеренно опускает сопоставления activity, ip и code.
Методы¶
start(string $context, int $timeout = 60): void
Запускает сеанс PHP (если он еще не запущен), устанавливает активный контекст и запускает checkTimeout() для завершения устаревших сеансов. Контекст должен быть 'admin', 'reseller' или 'player'.
requireAuth(?string $loginUrl = null): void
Проверяет наличие аутентифицированного сеанса. Если запрос направлен напрямую на session.php, возвращает ответ в формате JSON {"result": true/false} (используется для опроса сеанса AJAX). В противном случае перенаправляет не прошедших проверку пользователей на страницу входа в систему. В случае успеха вызывает touch() для обновления временной метки действия.
isAuthenticated(): bool
Проверка на отсутствие блокировки. Возвращает значение true, если сеанс был запущен и установлен ключ auth.
getUser(): mixed
Возвращает значение, сохраненное в ключе сеанса auth (идентификатор пользователя для администратора/реселлера или идентификатор строки для игрока), или null, если аутентификация не пройдена.
getValue(string $name): mixed
Возвращает значение сеанса по его логическому имени (auth, activity, ip, code, verify). Логическое имя сопоставляется с фактическим ключом $_SESSION на основе текущего контекста.
setValue(string $name, mixed $value): void
Устанавливает значение сеанса по логическому имени.
login(mixed $hash, ?string $ip = null): void
Создает аутентифицированный сеанс, задавая значения auth и activity. При необходимости сохраняет IP-адрес клиента.
destroy(): void
Очищает все ключи сеанса для текущего контекста. Если никакой другой контекст не активен (проверяет противоположный контекст администратора/реселлера), уничтожает весь сеанс PHP.
clearContext(string $context): void
Очищает все ключи сеанса для определенного контекста, не разрушая сеанс. Простая замена устаревшего destroySession($type).
touch(): void
Обновляет $_SESSION[$activityKey] до текущей временной метки и вызывает session_write_close(), чтобы снять блокировку сеанса.
getContext(): ?string
Возвращает текущую контекстную строку ('admin', 'reseller', 'player') или null, если она не задана.
Поведение по истечении времени ожидания¶
SessionManager::DEFAULT_TIMEOUT равно 60 минутам. Метод checkTimeout() (вызываемый автоматически start()) сравнивает время, прошедшее с момента last_activity. Если время ожидания превышено, все ключи сеанса, зависящие от контекста, сбрасываются, что приводит к выходу пользователя из системы.
Поскольку контекст игрока не имеет ключа activity в карте ключей, проверка тайм-аута не применяется к сессиям игрока.
Охранник с применением грубой силы¶
Файл: src/Core/Auth/BruteforceGuard.php
Централизованное ограничение скорости и защита от перебора. Все методы используют состояние на основе файла, хранящееся в FLOOD_TMP_PATH (/home/xc_vm/tmp/flood/). Разрешенные IP-адреса (IP-адреса сервера) и IP-адреса, указанные в параметре flood_ips_exclude, всегда исключаются.
checkFlood(?string $ip = null, bool $useCachedMode = false): void¶
Скорость - ограничивает количество запросов по IP-адресу в пределах настраиваемого временного интервала.
- Настройки:
flood_limit(максимальное количество запросов),flood_seconds(размер окна). - Файл состояния:
FLOOD_TMP_PATH . $ip- сохраняет объект JSON с числомrequestsи временной меткойlast_request. - Поведение: Отслеживает количество запросов в пределах временного окна. Если количество превышает
flood_limit, IP-адрес блокируется (заносится в таблицуblocked_ipsили передается через Redis в кэшированном/потоковом режиме). Файл состояния удаляется после блокировки. - Используется: Вход игрока (вызывается при каждой неудачной попытке входа в систему), конечные точки потоковой передачи.
checkBruteforce(?string $ip = null, ?string $mac = null, ?string $username = null, bool $useCachedMode = false): void¶
Обнаруживает атаки методом перебора на основе количества уникальных MAC-адресов или имен пользователей, обнаруженных с одного IP-адреса.
- Настройки:
bruteforce_mac_attempts,bruteforce_username_attempts( максимальное количество уникальных значений),bruteforce_frequency(временной интервал в секундах). - Файл состояния:
FLOOD_TMP_PATH . $ip . '_mac'илиFLOOD_TMP_PATH . $ip . '_user'- сохраняет попытки в виде пар{term: timestamp}. - Поведение: Попытки с истекшим сроком действия (за пределами частотного диапазона) отсекаются с помощью
truncateAttempts(). Если количество уникальных запросов превышает допустимое, IP-адрес блокируется. - Используется: Конечные точки потоковой аутентификации.
checkAuthFlood(array $user, ?string $ip = null): void¶
Ограничивает скорость запросов на аутентификацию для конкретной комбинации пользователь +IP. Предназначен для ограничения повторных попыток авторизации без полной блокировки.
- Настройки:
auth_flood_limit(максимальное количество попыток),auth_flood_seconds(окно),auth_flood_sleep(задержка в секундах при блокировке). - Файл состояния:
FLOOD_TMP_PATH . $userId . '_' . $ip- сохраняет попытки в виде индексированных временных меток плюс необязательную временную меткуblock_until. - Поведение: Когда количество попыток превышает допустимое значение, устанавливается временная метка
block_until. Последующие запросы в течение периода блокировки задерживаются наauth_flood_sleepсекунды (черезsleep()). IP-адрес не блокируется навсегда. Пользователи Restreamer (is_restreamer) освобождаются от этого требования. - Используется: Потоковая аутентификация.
truncateAttempts(array $attempts, int $frequency, bool $list = false): array¶
Отфильтровывает просроченные попытки из массива отслеживания. Если значение $list равно true, массив обрабатывается как индексированный (для checkAuthFlood); в противном случае как ассоциативный с ключом по термину (для checkBruteforce).
Блокирующий механизм¶
Когда IP-адрес заблокирован:
- Обычный режим: Выполняет вставку в таблицу базы данных
blocked_ipsс указанием причины (FLOOD ATTACKилиBRUTEFORCE MAC/USER ATTACK) и обновляет кэшBlocklistService. - Режим кэширования/потоковой передачи (
$useCachedMode = true): Устанавливает сигнал Redis (bruteforce_attack/$ipилиflood_attack/$ip) черезRedisManager::setSignal()для блокировки потокового контекста без записи в базу данных. - В обоих режимах выполняется касание файла-маркера
FLOOD_TMP_PATH . 'block_' . $ipдля быстрой проверки на уровне файловой системы.
Безопасность сеанса¶
Настройка файлов cookie¶
В контексте начальной загрузки администратора сеансовые файлы cookie устанавливаются в соответствии со строгой политикой SameSite:
Проверка хэша¶
Проверяющий хэш ($_SESSION['verify'] / $_SESSION['rverify'] / $_SESSION['pverify']) вычисляется следующим образом:
Это значение сравнивается с текущими значениями базы данных при каждой загрузке страницы. Если администратор изменяет пароль пользователя (что изменяет сохраненный хэш), все существующие сеансы для этого пользователя автоматически становятся недействительными, поскольку проверяемый хэш больше не будет совпадать.
Для логинов администратора и торгового посредника пароль повторно хэшируется во время входа в систему, поэтому используется $rCrypt (новый хэш). Для логинов игроков используется существующий сохраненный хэш $rUserInfo['password'].
Обработка изменений IP-адреса¶
Два параметра управляют поведением при изменении IP-адреса:
| Установка | Эффект |
|---|---|
ip_logout |
Если включено, завершает сеанс при изменении IP-адреса клиента (точного или из подсети, в зависимости от ip_subnet_match). |
ip_subnet_match |
Когда этот параметр включен, сравниваются только первые три октета IP-адреса, вместо того чтобы требовать точного совпадения. Позволяет пользователям с динамическими IP-адресами в пределах одной подсети сохранять сеанс связи. |
Когда ip_logout отключено и IP-адрес изменяется, сохраненный IP-адрес сеанса автоматически обновляется до нового IP-адреса.
Регистрация входа в систему¶
Когда параметр save_login_logs включен, все попытки входа в систему (успешные и неудачные) записываются в таблицу login_logs:
INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`)
VALUES($type, $codeId, $userId, $status, $ip, $timestamp);
| Колонка | Описание |
|---|---|
type |
ADMIN или RESELLER |
access_code |
Идентификатор текущего кода доступа |
user_id |
Идентификатор пользователя (0 для неверных учетных данных) |
status |
SUCCESS, INVALID_LOGIN, INVALID_CODE, NOT_ADMIN, DISABLED |
login_ip |
IP-адрес клиента |
date |
Временная метка Unix |
Логины игроков не записываются в login_logs.
Авторизация (после входа в систему)¶
После аутентификации два дополнительных уровня авторизации определяют, к чему пользователь может получить доступ:
Authorization¶
Файл: src/Core/Auth/Authorization.php
Авторизация на уровне объекта. Проверяет, есть ли у текущего пользователя разрешение на доступ к определенному ресурсу (пользователь, поток и т.д.) на основе иерархии владельцев реселлеров и групповых разрешений.
Authorization::hasResellerPermissions($type)-- устанавливает флажок единственного разрешения на$rPermissions.Authorization::check($type, $id)-- проверяет доступ к определенному ресурсу по типу и идентификатору.
PageAuthorization¶
Файл: src/Core/Auth/PageAuthorization.php
Управление доступом на уровне страницы. Определяет, разрешают ли групповые права текущего пользователя доступ к определенной странице панели администратора или торгового посредника.
PageAuthorization::checkResellerPermissions($page)-- сопоставляет названия страниц с флагами требуемых разрешений и возвращает, разрешен ли доступ.
Связанные файлы¶
| Файл | Цель |
|---|---|
src/Core/Auth/Authenticator.php |
Логика входа администратора и реселлера в систему, хэширование паролей |
src/Core/Auth/SessionManager.php |
Унифицированный сеансовый API с сопоставлением контекстных ключей |
src/Core/Auth/BruteforceGuard.php |
Ограничение скорости и защита от перебора |
src/Core/Auth/Authorization.php |
Проверки авторизации на уровне объекта |
src/Core/Auth/PageAuthorization.php |
Управление доступом на уровне страницы |
src/Public/Controllers/Player/PlayerLoginController.php |
Процесс входа игрока в систему с проверкой безопасности |
src/Public/Views/admin/functions.php |
Проверка сеанса администратора при каждой загрузке страницы |
src/Public/Views/admin/session.php |
Тайм-аут сеанса администратора и проверка сеанса AJAX |
src/Infrastructure/Bootstrap/reseller_functions.php |
Проверка сеанса реселлера при каждой загрузке страницы |
src/Domain/User/UserRepository.php |
Поиск учетных данных (getAuthUserByCredentials) |
src/bootstrap.php |
Определения констант состояния, контексты начальной загрузки |
src/Core/Config/Paths.php |
FLOOD_TMP_PATH определение |