Глава 9. Безопасность и оптимизация производительности

07-07-26 15:09

В этой главе рассматриваются встроенные механизмы обеспечения безопасности Seditio CMS (фильтрация ввода, защита от CSRF/XSS и SQL-инъекций), а также инструменты кэширования и оптимизации работы с базой данных для повышения быстродействия сайта.


9.1. Фильтрация входящих данных (sed_import)

В Seditio CMS действует принцип: «никогда не доверяй входящим данным». Любая переменная, получаемая из внешних источников (запросы GET, POST, файлы Cookie), перед использованием должна быть импортирована и очищена с помощью системной функции sed_import() (описана в system/functions.php).

9.1.1. Сигнатура и назначение параметров

Функция имеет следующую структуру:

function sed_import($name, $source, $filter, $maxlen = 0, $dieonerror = false)
  • $name (string) — имя импортируемой переменной (ключ в массивах $_GET, $_POST или $_COOKIE).
  • $source (string) — источник данных (откуда считывать значение).
  • $filter (string) — тип применяемого фильтра очистки.
  • $maxlen (int, необязательный) — ограничение максимальной длины строки. Если значение превышает $maxlen$maxlen > 0), строка автоматически обрезается с помощью мультибайтовой функции mb_substr(). По умолчанию — 0 (без ограничений).
  • $dieonerror (bool, необязательный) — флаг критической обработки. Если равен true и переданное значение не проходит валидацию выбранным фильтром, скрипт немедленно прекращает выполнение с выводом ошибки sed_diefatal('Wrong input.'). По умолчанию — false.

9.1.2. Источники входящих данных

Параметр $source принимает одно из четырёх строковых значений:

  • 'G' — данные считываются из глобального массива $_GET.
  • 'P' — данные считываются из глобального массива $_POST.
  • 'C' — данные считываются из глобального массива $_COOKIE.
  • 'D' — прямое использование значения (DIRECT). В этом случае в качестве $name передается готовая PHP-переменная, которую требуется просто отфильтровать по заданным правилам.

9.1.3. Справочник фильтров очистки

В зависимости от ожидаемого типа данных, параметр $filter может принимать следующие значения:

Фильтр Описание логики очистки и валидации
INT Проверяет, является ли значение целым числом. При успешной валидации приводит значение к типу (int).
NUM Проверяет, является ли значение числовым. Приводит к числу с плавающей точкой или целому ($v + 0).
TXT Безопасный текст. Если строка содержит символ <, она автоматически экранируется через htmlspecialchars($v, ENT_QUOTES, 'UTF-8').
SLU ЧПУ-слаг. Разрешает только латинские символы, цифры, знаки подчеркивания, дефисы, знаки равенства и слэш: a-zA-Z0-9_\-=/. Все остальные символы вырезаются.
ALP Только алфавитные символы. Очищает строку от любых символов, кроме латинских букв (использует вспомогательную функцию sed_alphaonly()).
ALS Заголовки и имена. Разрешает буквы, цифры, пробелы и дефисы. Любые другие символы заменяются знаком подчеркивания _.
PSW Безопасный пароль. Вырезает кавычки (', "), знак амперсанда (&) и угловые скобки (<, >). Максимальная длина жестко ограничивается 32 символами.
H32 Хэш (например, MD5). Разрешает только латинские буквы и цифры. Длина обрезается до 32 символов.
HTR Доверенный HTML (HTML Trusted). Строка возвращается без какой-либо фильтрации в исходном виде. Применяется только для контента от администраторов.
HTM HTML-код, подлежащий обработке плагинами очистки через хук import.filter (например, плагином HTMLPurifier).
ARR Массив. Возвращает массив без изменений. Фильтрация элементов должна выполняться вручную.
BOL Булево значение. Если значение равно '1', 'on', true или 1, возвращает true. Для '0', 'off', false или 0 возвращает false. Во всех остальных случаях возвращает false.
LVL Уровень пользователя. Числовое значение строго от 0 до 100.
NOC Без проверки (No Check). Возвращает сырую строку без изменений.

9.1.4. Логика обработки некорректных значений

Если входящее значение не проходит проверку регулярным выражением или условием фильтра:

  1. Выполняется автоматическая запись инцидента в журнал безопасности с помощью функции sed_log_sed_import().
  2. Если $dieonerror равен true, выполнение прерывается фатальной ошибкой.
  3. Если выполнение продолжается, функция возвращает безопасное значение по умолчанию (для большинства строковых фильтров возвращается предварительно очищенная строка, для BOLfalse, для числовых — null).

Пример безопасного импорта параметров в коде модуля:

// Безопасное получение ID страницы (GET)
$id = sed_import('id', 'G', 'INT');

// Безопасное получение текста комментария (POST)
$text = sed_import('rtext', 'P', 'TXT', 1000); 

// Безопасный импорт с аварийным завершением
$category = sed_import('c', 'G', 'SLU', 64, true);

9.2. Защита от CSRF и XSS-атак

Межсайтовая подделка запроса (CSRF) — атака, при которой пользователя заставляют выполнить нежелательные действия на веб-ресурсе, где он авторизован. Seditio CMS блокирует эти атаки с помощью токенизации запросов.

9.2.1. Генерация токена безопасности (sed_sourcekey)

При инициализации сеанса в файле system/common.php для каждого посетителя генерируется уникальный сессионный ключ:

  • Для гостей: Создается случайный MD5-хэш с помощью функции sed_unique() и записывается в сессию:
    $_SESSION['guest_sourcekey'] = md5(sed_unique(16));
    
  • Для авторизованных пользователей: Ключ генерируется динамически на основе секретного индивидуального ключа пользователя и времени его последнего визита:
    $usr['sourcekey'] = md5($row['user_secret'] . $row['user_lastvisit']);
    

Функция sed_sourcekey() в system/functions.php считывает этот ключ, берет первые 6 символов, приводит к верхнему регистру (для гостей добавляется префикс GUEST_) и возвращает полученный токен.

9.2.2. Защита GET-запросов (sed_check_xg)

Любые действия, изменяющие состояние системы через GET-запросы (например, удаление страницы, очистка кэша, переключение статуса), должны сопровождаться проверочным параметром x в URL:

<a href="index.php?module=page&m=delete&id=5&x={PHP.sys.xg}">Удалить страницу</a>

При обработке этого запроса контроллер ядра в обязательном порядке вызывает валидатор:

sed_check_xg(); // Сравнит $_GET['x'] с токеном sed_sourcekey() и прервет работу при несовпадении

9.2.3. Защита POST-форм (sed_check_xp)

Все POST-формы на сайте должны передавать токен безопасности в скрытом поле x. В HTML-шаблоне:

<form action="index.php?module=comments&m=add" method="post">
    <input type="hidden" name="x" value="{PHP.sys.xp}" />
    <textarea name="rtext"></textarea>
    <button type="submit">Отправить</button>
</form>

В PHP-контроллере обработчика:

sed_check_xp(); // Защитит форму от отправки сторонними скриптами

9.2.4. Безопасность AJAX-вызовов (sed_check_csrf)

При выполнении асинхронных AJAX-запросов токен безопасности передается в HTTP-заголовке X-Seditio-CSRF. На стороне сервера валидация выполняется с помощью функции sed_check_csrf():

if (!sed_check_csrf()) {
    sed_diefatal('Invalid CSRF token for AJAX request.');
}

9.3. Защита от SQL-инъекций

SQL-инъекция — атака, при которой внедряется вредоносный SQL-код во входящие запросы к базе данных.

9.3.1. Экранирование через sed_sql_prep

Для подготовки строковых значений перед их вставкой в SQL-запрос используется функция sed_sql_prep() (или встроенные методы экранирования СУБД):

function sed_sql_prep($string)

Она экранирует спецсимволы SQL (одинарные кавычки, управляющие символы) для предотвращения выхода за границы SQL-контекста.

9.3.2. Рекомендации по написанию безопасных запросов

  1. Числовые параметры: Все числовые переменные должны принудительно преобразовываться к типу (int) или (float) либо импортироваться через фильтр INT / NUM:
    // БЕЗОПАСНО:
    $id = (int)$id;
    $sql = sed_sql_query("SELECT * FROM $db_pages WHERE page_id = $id");
    
  2. Строковые параметры: Все строки должны оборачиваться в sed_sql_prep() и помещаться внутри запроса в одинарные кавычки:
    // БЕЗОПАСНО:
    $title = sed_sql_prep($title);
    $sql = sed_sql_query("SELECT * FROM $db_pages WHERE page_title = '$title'");
    

[!CAUTION] Прямая склейка строк: Никогда не вставляйте нефильтрованные глобальные массивы ($_GET, $_POST) напрямую в SQL-запрос. Запросы вида SELECT * FROM table WHERE field = '. $_GET['val']. ' категорически запрещены и представляют критическую уязвимость!


9.4. Оптимизация производительности и кэширование

Для снижения нагрузки на сервер базы данных и ускорения отдачи страниц в Seditio CMS реализована гибридная система кэширования.

9.4.1. Встроенное кэширование в базу данных (DB Cache)

Включение кэширования производится в панели администратора (раздел «Конфигурация» → «Главные настройки» → «Внутренний Кэш»), после чего ядро сохраняет статус $cfg['cache'] = 1 в системной таблице конфигураций (sed_config). В PHP-коде текущее состояние кэширования считывается из глобального массива конфигурации $cfg['cache']. При включенной опции система сохраняет ресурсоемкие результаты (например, списки меню, облака тегов, результаты работы плагинов) в системную таблицу базы данных $db_cache (обычно sed_cache).

Для управления кэшем используются следующие функции:

  • sed_cache_store($name, $value, $expire, $auto = 1) — сохраняет значение под уникальным именем $name.
    • $value — любые структуры данных (массивы, объекты, строки), которые внутри автоматически сериализуются через serialize().
    • $expire — время жизни кэша в секундах.
    • $auto — флаг автозагрузки (описан ниже).
  • sed_cache_get($name, $expire = true) — извлекает данные из кэша. Если срок жизни кэша истек (и параметр $expire равен true), функция вернет false.
  • sed_cache_clear($name) — удаляет конкретную запись кэша по имени.
  • sed_cache_clearall() — полностью очищает всю таблицу кэша.

9.4.2. Механизм автозагрузки кэша (c_auto)

Для минимизации обращений к базе данных при загрузке каждой страницы Seditio CMS использует оптимизацию автозапускаемых кэшей. При записи кэша с флагом $auto = 1 запись помечается в БД. Во время инициализации ядра в файле system/common.php выполняется один групповой SQL-запрос:

SELECT c_name, c_value FROM sed_cache WHERE c_auto = 1

Все полученные данные десериализуются и импортируются в глобальную оперативную память PHP за один шаг. После этого вызовы sed_cache_get() для автозагружаемых элементов выполняются мгновенно без создания новых SQL-запросов к серверу БД.

9.4.3. Оптимизация клиентского вывода (cleanup)

На уровне шаблонизатора реализована опция базовой очистки и нормализации пробельных символов в HTML-страницах перед их отдачей клиенту. При активации свойства cleanupEnabled (через статический конфигуратор XTemplate::configure()):

  • У каждой строки результирующего HTML удаляются пробелы и табуляции в начале и в конце.
  • Переносы строк и символы табуляции вокруг HTML-тегов (< и >) преобразуются или вырезаются для слияния разметки в единый поток.
  • Множественные пробелы в тексте и внутри тегов (между атрибутами) сжимаются в один пробел.

[!WARNING] Отсутствие специализированной минификации: Метод cleanup() не производит удаление HTML-комментариев и не сжимает inline JavaScript/CSS по правилам минификации. Поскольку переносы строк удаляются вокруг тегов и сжимаются в тексте, использование однострочных JavaScript-комментариев вида // может привести к синтаксическим ошибкам (весь последующий код склеится в комментируемую строку). В inline-скриптах шаблонов рекомендуется использовать исключительно блочные комментарии /* ... */.

Ratings:
(0.00)

No comments yet