Перейти к содержанию

Проверка и санитарная обработка входных данных

XC_VM использует двухуровневую защиту для входящих данных запроса. Во-первых, передача глобальная санитарная обработка удаляет опасный контент со всех PHP суперглобальных объектов во время начальной загрузки, перед запуском любого кода приложения. Во-вторых, уровень проверка на уровне действий проверяет наличие обязательных полей перед выполнением бизнес-логики.

Оба слоя реализованы в виде src/Core/Validation/InputValidator.php.


Глобальный поток санитарной обработки

Очистка выполняется автоматически во время начальной загрузки. Когда вызывается LegacyInitializer::initCore()src/Core/Init/LegacyInitializer.php), перед любым кодом контроллера или службы выполняются следующие действия:

LegacyInitializer::initCore()
  |
  +-- InputValidator::cleanGlobals($_GET)
  +-- InputValidator::cleanGlobals($_POST)
  +-- InputValidator::cleanGlobals($_SESSION)
  +-- InputValidator::cleanGlobals($_COOKIE)
  |
  +-- $input = InputValidator::parseIncomingRecursively($_GET)
  +-- RequestManager::set(InputValidator::parseIncomingRecursively($_POST, $input))

После выполнения этой последовательности все необработанные суперглобальные данные были обработаны на месте, и объединенные/очищенные данные GET+POST доступны через RequestManager.

Контекст потоковой передачи (LegacyInitializer::initStreaming()) выполняет ту же последовательность очистки с использованием класса Request, который предоставляет эквивалентные методы для пути начальной загрузки потоковой передачи.

Чистые глобальные значения(&$rData, $rIteration = 0)

Рекурсивно обходит заданный суперглобальный массив и удаляет опасное содержимое. Применяется к $_GET, $_POST, $_SESSION, и $_COOKIE.

Извлекает следующее из каждого скалярного значения:

Угроза Узор удален Замена
Ввод нулевого байта \0 (chr 0) удаленный
Обход пути ../ ../ (в кодировке HTML)
Переопределение RTL (подмена пользовательского интерфейса) ‮ удаленный

Рекурсия ограничена 10 уровнями, чтобы предотвратить исчерпание стека из-за глубоко вложенных входных данных.

Синтаксический анализ выполняется рекурсивно(&$rData, $rInput, $rIteration = 0)

Рекурсивно обрабатывает данные GET и POST, применяя очистку ключей и значений к каждому листу. Для массивов выполняется рекурсия на глубину до 20 уровней. Для скалярных значений применяется parseCleanKey() к ключу и parseCleanValue() к значению.

Объединенный результат (сначала ПОЛУЧИТЬ, затем опубликовать с наложением) сохраняется в RequestManager для использования на протяжении всего жизненного цикла запроса.

parseCleanKey($rKey)

Очищает ключи массива, чтобы предотвратить внедрение с помощью имен ключей:

  1. URL-расшифровывает, а HTML-экранирует ключ (htmlspecialchars(urldecode(...)))
  2. Удаляет последовательности с двумя точками (.. -> '')
  3. Strips __dunder__-маркеры стиля с помощью регулярного выражения
  4. Проверяет соответствие допустимому набору символов: символы word, точки, дефисы, подчеркивания

Значение parseCleanValue($rValue)

Выполняет очистку скалярных значений за несколько проходов:

Шаг Что он делает
Неэкранированный пейзаж stripslashes() и вернуть   в исходное положение
Нормализовать новые строки Преобразовать \r\n, \n\r, \r в \n
Защита HTML-комментариев <!-- становится &#60;&#33;--, --> становится --&#62;
Нейтрализация тегов скрипта <script (без учета регистра) становится &#60;script
Нормализация объекта Исправлены объекты с двойным кодированием и искаженные числовые объекты
Отделка Начальные и конечные пробелы удалены

Проверка на уровне действий

проверить()

InputValidator::validate(string $rAction, array $rData): bool

Проверяет наличие минимально необходимых полей для данного действия. Возвращает true, если данные приемлемы, false, если необходимые поля отсутствуют или оформлены неправильно. Контроллеры должны вызывать это перед отправкой данных на уровни службы/хранилища.

if (!InputValidator::validate($action, $data)) {
    // reject with validation error
}

validateOrFail()

InputValidator::validateOrFail(string $rAction, array $rData): ?array

Удобная оболочка для validate(). Возвращает null, если данные верны, или массив ошибок, если проверка не удалась:

$error = InputValidator::validateOrFail($action, $data);
if ($error !== null) {
    // $error = ['status' => STATUS_INVALID_INPUT, 'data' => $data]
    return $error;
}

Подтверждающие идентификаторы($ids)

InputValidator::confirmIDs($ids)   // untyped params/return; yields a filtered array of positive int IDs

Фильтрует массив, чтобы он содержал только целые положительные идентификаторы. Любое значение, в котором пропущено значение intval($id) <= 0. Широко используется в кодовой базе (более 30 сайтов для звонков) везде, где необходимо очистить списки идентификаторов, предоставленные пользователем, перед запросами к базе данных.

$safeIds = InputValidator::confirmIDs($userSuppliedIds);
// [1, 42, 7] -- negative, zero, and non-numeric values removed

Ссылка на действие проверки

В методе validate() вместо названия действия используется оператор switch. Ниже действия сгруппированы по функциональным областям.

контент-менеджмент

Потоки и каналы

Действие Обязательные для заполнения поля Записи
processStream флаг stream_display_name ИЛИ review ИЛИ $_FILES['m3u_file'] Любой из трех параметров удовлетворяет требованиям проверки
processChannel флаг stream_display_name ИЛИ review ИЛИ $_FILES['m3u_file'] Те же правила, что и в processStream
processRadio флаг stream_display_name ИЛИ review ИЛИ $_FILES['m3u_file'] Те же правила, что и в processStream

Фильмы / VOD

Действие Обязательные для заполнения поля Записи
processMovie флаг stream_display_name ИЛИ review ИЛИ $_FILES['m3u_file'] Те же правила, что и в processStream

Сериалы и эпизоды

Действие Обязательные для заполнения поля Записи
processSeries title Требуется указать название серии
processEpisode series (непустой) И season_num (числовой) И (multi флаг ИЛИ episode числовой) Сложная многопутевая проверка

Организация

Букеты

Действие Обязательные для заполнения поля Записи
processBouquet bouquet_name Требуемый скаляр
reorderBouquet stream_order_array Необходимо декодировать в массив JSON
sortBouquets bouquet_order_array Необходимо декодировать в массив JSON

Категории

Действие Обязательные для заполнения поля Записи
processCategory category_name, category_type Оба необходимых
orderCategories categories Необходимо декодировать в массив JSON

Группы и коды

Действие Обязательные для заполнения поля Записи
processGroup group_name Требуемый скаляр
processGroupLegacy group_name То же, что и processGroup
processCode code Требуемый скаляр
processPackage package_name Требуемый скаляр

EPG

Действие Обязательные для заполнения поля Записи
processEPG epg_name, epg_file Оба необходимых

Устройства и линии

Действие Обязательные для заполнения поля Записи
processMAG mac Требуется MAC-адрес
processEnigma mac Требуется MAC-адрес
setChannelOrder stream_order_array Необходимо декодировать в массив JSON

Профили

Действие Обязательные для заполнения поля Записи
processProfile profile_name Требуемый скаляр

Поставщики услуг

Действие Обязательные для заполнения поля Записи
processProvider ip, port, username, password, name Все пять необходимых
processISP isp Требуемый скаляр
processUA user_agent Требуемый скаляр

Безопасность

Действие Обязательные для заполнения поля Записи
blockIP ip Требуется IP-адрес
processRTMPIP ip Требуется IP-адрес

Управление сервером

Действие Обязательные для заполнения поля Записи
processServer server_name, server_ip Оба необходимых
processProxy server_name, server_ip То же, что и processServer
installServer ssh_port, root_password Оба необходимых
moveStreams content_type, source_server, replacement_server Все три необходимых
replaceDNS old_dns, new_dns Оба необходимых
orderServers server_order Необходимо декодировать в массив JSON

Папки с записями и просмотром

Действие Обязательные для заполнения поля Записи
scheduleRecording title, source_id Оба необходимых
processWatchFolder folder_type, selected_path, server_id Все три необходимых

Массовые операции (полезная нагрузка в виде массива JSON)

Для всех массовых операций в указанном поле требуется массив в кодировке JSON. Поле должно быть преобразовано в допустимый массив PHP.

Действие Поле JSON
massEditEpisodes streams
massEditMovies streams
massEditRadios streams
massEditStreams streams
massEditChannels streams
massDeleteStreams streams
massEditSeries series
massDeleteSeries series
massEditLines users_selected
massEditUsers users_selected
massEditMags devices_selected
massEditEnigmas devices_selected
massDeleteMovies movies
massDeleteLines lines
massDeleteUsers users
massDeleteStations radios
massDeleteMags mags
massDeleteEnigmas enigmas
massDeleteEpisodes episodes

Аварийное поведение по умолчанию

Действия, явно не указанные в инструкции switch, попадают в return true, что означает, что они всегда проходят проверку. Это сделано намеренно - эти действия либо не содержат обязательных полей на уровне gate, либо выполняют свою собственную проверку на более глубоком уровне бизнес-логики.

Явно выполняемые сквозные действия (указан в переключателе как return true):

  • processUser
  • processLine
  • processHMAC
  • editAdminProfile
  • editSettings
  • editBackupSettings
  • editCacheCron
  • editPlexSettings
  • editWatchSettings
  • processPlexSync
  • processLogin
  • submitTicket

Неявно передаваемые действия (вообще отсутствует в переключателе, используется значение по умолчанию return true):

Любая строка действия, не соответствующая case, также вернет значение true. Если для нового действия требуется стробирование ввода, необходимо явно добавить регистр.


Используемые шаблоны проверки

Метод validate() последовательно использует небольшой набор шаблонов:

Шаблон Цель Пример
!empty($rData['field']) Обязательное скалярное поле (ненулевое, непустое, ненулевое значение) !empty($rData['bouquet_name'])
is_numeric($rData['field'] ?? null) Числовая проверка с резервным копированием, безопасным для null is_numeric($rData['season_num'] ?? null)
is_array(json_decode($rData['field'] ?? '', true)) Строка JSON, которую необходимо декодировать в массив is_array(json_decode($rData['streams'] ?? '', true))
isset($rData['field']) Проверка наличия поля (значение может быть пустым/ложным) isset($rData['review'])
isset($_FILES['field']) Проверка наличия загружаемого файла isset($_FILES['m3u_file'])
ИЛИ условия Многопутевая проверка (удовлетворяет любой из путей) !пусто($rData['имя_потока']) \|\|isset($rData['обзор']) \|\|isset($_FILES['m3u_file'])

Добавление проверки для нового действия

Добавьте case к switch в src/Core/Validation/InputValidator.php:

case 'myNewAction':
    return !empty($rData['required_field'])
        && is_numeric($rData['numeric_field'] ?? null);

Методические рекомендации:

  • На этом уровне проверяйте только минимально необходимые входные данные. Сохраняйте правила, относящиеся к предметной области (проверка формата, бизнес-ограничения, проверка уникальности), на уровне сервиса.
  • Используйте !empty() для требуемых скаляров, is_numeric() для числовых полей и is_array(json_decode(..., true)) для полезной нагрузки массива JSON.
  • Для действий, которые принимают загрузку файлов в качестве альтернативы полям формы, укажите isset($_FILES['field']) в качестве условия ИЛИ.
  • Если действие не требует проверки на уровне шлюза, добавьте его в явный сквозной блок с return true, чтобы будущие разработчики знали, что это упущение является намеренным, а не случайным.

Связанные файлы

Файл Цель
src/Core/Validation/InputValidator.php Вся логика санитарной обработки и проверки
src/Core/Init/LegacyInitializer.php Вызывающий элемент Bootstrap, запускающий очистку с помощью initCore()
src/Core/Http/RequestManager.php Хранит обработанные, объединенные данные GET+POST
src/Public/Controllers/ Контроллеры, которые вызывают validate() / validateOrFail() перед бизнес-логикой