Проверка и санитарная обработка входных данных¶
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)¶
Очищает ключи массива, чтобы предотвратить внедрение с помощью имен ключей:
- URL-расшифровывает, а HTML-экранирует ключ (
htmlspecialchars(urldecode(...))) - Удаляет последовательности с двумя точками (
..->'') - Strips
__dunder__-маркеры стиля с помощью регулярного выражения - Проверяет соответствие допустимому набору символов: символы word, точки, дефисы, подчеркивания
Значение parseCleanValue($rValue)¶
Выполняет очистку скалярных значений за несколько проходов:
| Шаг | Что он делает |
|---|---|
| Неэкранированный пейзаж | stripslashes() и вернуть   в исходное положение |
| Нормализовать новые строки | Преобразовать \r\n, \n\r, \r в \n |
| Защита HTML-комментариев | <!-- становится <!--, --> становится --> |
| Нейтрализация тегов скрипта | <script (без учета регистра) становится <script |
| Нормализация объекта | Исправлены объекты с двойным кодированием и искаженные числовые объекты |
| Отделка | Начальные и конечные пробелы удалены |
Проверка на уровне действий¶
проверить()¶
Проверяет наличие минимально необходимых полей для данного действия. Возвращает true, если данные приемлемы, false, если необходимые поля отсутствуют или оформлены неправильно. Контроллеры должны вызывать это перед отправкой данных на уровни службы/хранилища.
validateOrFail()¶
Удобная оболочка для 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):
processUserprocessLineprocessHMACeditAdminProfileeditSettingseditBackupSettingseditCacheCroneditPlexSettingseditWatchSettingsprocessPlexSyncprocessLoginsubmitTicket
Неявно передаваемые действия (вообще отсутствует в переключателе, используется значение по умолчанию 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() перед бизнес-логикой |