MORLIX JOURNAL
Безопасность CMS: защита от XSS, CSRF и SQL-инъекций
Как устроить защиту CMS от XSS, CSRF и SQL-инъекций: границы доверия, практические меры и проверка на тестовом стенде.
Безопасность CMS строится вокруг границ доверия: кто может отправить данные, какие действия разрешены пользователю и как приложение обрабатывает информацию перед выводом в браузер или передачей в базу. XSS, CSRF и SQL-инъекция затрагивают разные границы, поэтому одной настройкой закрыть все три риска нельзя.
Для защиты пользовательского текста нужно безопасное отображение с учётом контекста; для действий с авторизацией через cookie — проверка подлинности запроса и прав; для работы с базой — параметризованные запросы и ограничение привилегий. Эти меры дополняют обновления, резервное копирование и контроль расширений. Ниже — практический разбор для владельцев сайтов, разработчиков и команд сопровождения.
Три угрозы: где возникает проблема
| Угроза | Граница доверия | Пример риска | Основная защита |
|---|---|---|---|
| XSS | Данные попадают в документ или код браузера | Небезопасно выведенное содержимое комментария выполняется в контексте сайта | Контекстное экранирование и проверенная очистка разрешённого HTML |
| CSRF | Браузер отправляет запрос с учётными данными пользователя | Авторизованного редактора вынуждают выполнить нежелательное изменение | Проверка CSRF-токена и прав для изменяющих действий |
| SQL-инъекция | Ввод пользователя смешивается с синтаксисом запроса | Некорректная сборка запроса позволяет изменить его смысл | Параметризация значений и ограничение прав учётной записи базы |
Например, форма редактирования материала может одновременно принимать текст, отправлять изменяющий запрос и сохранять данные в БД. Ей нужны отдельные проверки для каждого этапа. При этом права редактора проверяются на сервере независимо от того, видит ли он кнопку в интерфейсе.
Где в типичной системе проходят эти границы, удобно рассмотреть в статье об архитектуре CMS на PHP: маршрут, контроллер, сервис, доступ к данным и шаблон решают разные задачи.

XSS: как безопасно выводить пользовательские данные
Cross-site scripting возникает, когда недоверенные данные обрабатываются браузером как исполняемый или активный код в контексте сайта. Источником могут быть комментарий, профиль, сохранённый материал, параметр адреса или данные внешней интеграции. Уязвимость способна затронуть как публичную страницу, так и панель, где редактор просматривает присланное содержимое.
Экранируйте по месту вывода
Для обычного текста HTML используйте средства шаблонизатора с автоматическим экранированием. В PHP для демонстрации текстового контекста и корректно заключённого в кавычки HTML-атрибута можно применить htmlspecialchars:
<?php
$comment = 'A < B & C';
echo htmlspecialchars($comment, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
Этот пример показывает только преобразование строки для HTML, а не готовую защиту всех мест вывода. Контексты JavaScript, CSS и URL имеют другие правила. Не вставляйте произвольные значения непосредственно в исполняемый JavaScript или обработчики событий; передавайте данные через безопасные механизмы платформы. Для ссылок отдельно проверяйте разрешённые схемы и назначение адреса: одно лишь HTML-экранирование не делает произвольный URL безопасным.
Проверяйте не только текст, введённый сейчас: сохранённые данные, импорт и ответы внешних сервисов тоже остаются недоверенными. В браузерном коде для обычного текста предпочитайте API, который устанавливает текст, а не интерпретирует строку как HTML. Ищите места прямой вставки в документ, в том числе в темах и расширениях.
Если редактору нужен форматированный HTML
Иногда экранировать весь материал как обычный текст нельзя: редактору нужны ссылки и ограниченное форматирование. В таком случае применяйте поддерживаемый и настроенный HTML-санитайзер с разрешённым набором элементов, атрибутов и безопасных схем ссылок. Не пытайтесь заменить полноценный разбор HTML коротким регулярным выражением. Проверьте очистку при сохранении и при последующем использовании данных в других контекстах.
Политика Content Security Policy может уменьшить последствия части ошибок, но не заменяет безопасный вывод и корректную очистку. Флаги cookie HttpOnly и Secure также полезны, однако они не делают XSS безвредным: код в контексте страницы может взаимодействовать с доступными пользователю функциями.
CSRF: защищайте изменяющие запросы
Cross-site request forgery использует ситуацию, когда браузер автоматически прикладывает к запросу действующие учётные данные, например cookie сессии. Посторонняя страница может попытаться заставить браузер авторизованного пользователя отправить нежелательный запрос. Поэтому проверка «пользователь вошёл» отвечает не на все вопросы: нужно также убедиться, что запрос проходит предусмотренную защиту, а действие разрешено этому пользователю.
Токен и серверная авторизация
- Используйте встроенный механизм CSRF своего фреймворка или CMS. Для сессионной формы сервер создаёт непредсказуемый токен, связывает его с сессией и передаёт в форму.
- При получении изменяющего запроса сервер проверяет токен до выполнения операции, безопасно сравнивает его с ожидаемым значением и отклоняет отсутствие или ошибку. Токен не должен попадать в URL и журналы.
- Независимо проверьте идентичность пользователя, его роль и доступ к конкретному объекту. Корректный токен не даёт право редактировать чужой материал.
- Не выполняйте изменение состояния через обычный GET-запрос. Защитите не только формы в панели, но и фоновые запросы, если они используют ту же cookie-сессию.
Для API схема зависит от того, как передаются учётные данные. Не делайте вывод о защищённости только по формату JSON или наличию CORS: определите, может ли браузер автоматически приложить авторизацию, и примените подходящую защиту для каждого сценария. Внешние вебхуки с отдельной аутентификацией требуют собственной проверки подлинности, а не копирования формы входа.
Атрибут SameSite для cookie, проверка Origin или Referer и повторное подтверждение особо чувствительных действий могут служить дополнительными уровнями. Их поведение и исключения нужно проверить для своего приложения. Они не заменяют серверную проверку прав, а также не устраняют XSS: выполняющийся в контексте сайта вредоносный код способен действовать от имени пользователя.
SQL-инъекция: отделяйте данные от структуры запроса
Риск возникает, когда недоверенное значение становится частью SQL-команды через конкатенацию или интерполяцию. Вместо этого используйте параметризованные запросы: структура команды задаётся отдельно, а значения передаются драйверу как параметры. Проверьте, что это правило соблюдается в поиске, фильтрах, административных таблицах, отчётах и расширениях, а не только в форме входа.
Например, при выборе записей по идентификатору пользователя приложение готовит запрос с параметром, затем передаёт проверенное значение в механизм доступа к данным. Проверка типа и диапазона полезна для бизнес-правил, но не заменяет параметризацию. Функция «экранирования SQL» не должна становиться универсальным решением для всех запросов и драйверов.
Плейсхолдеры обычно предназначены для значений, а не для имён таблиц, столбцов или произвольного направления сортировки. Если пользователь выбирает сортировку, сопоставьте входной ключ с заранее заданным списком допустимых полей и вариантов направления; не подставляйте введённый им фрагмент SQL. Для динамического числа значений используйте предусмотренный библиотекой способ создания нужного числа параметров.
Отдельная учётная запись приложения должна иметь только необходимые права на базе. Не выводите пользователю трассировки и подробные сообщения SQL-ошибок; сохраняйте техническую диагностику в защищённом журнале без секретов. Меньшие привилегии снижают последствия ошибки, но не исправляют саму инъекцию.
Что должен проверить владелец CMS
Если вы не пишете код самостоятельно, попросите подрядчика показать процесс и результаты проверки на тестовой копии. Оценивать нужно не только ядро: тема, сторонний модуль, интеграция и пользовательский код могут обходить общие механизмы системы.
- Обновления. Составьте перечень ядра, расширений и окружения; назначьте ответственного за обновления, проверку совместимости и устранение известных проблем.
- Доступы. Выдавайте отдельные учётные записи и минимально необходимые роли, удаляйте ненужные доступы, включайте доступные средства усиления входа и контролируйте сессии.
- Данные и секреты. Храните конфигурацию и ключи вне публично доступных каталогов, ограничивайте права файлов и не передавайте секреты в письмах или журналах.
- Расширения. Уточните происхождение модулей, необходимость каждого, поддерживается ли он и кто отвечает за обновление. Удаление интерфейсной кнопки не заменяет проверку полномочий на сервере.
- Резервные копии. Защитите копии от постороннего доступа, храните их независимо от основного окружения и регулярно проверяйте восстановление на изолированном стенде.
При работе нескольких редакторов полезно отдельно разобрать роли, обновления и передачу доступа. Для интеграций с внешними агентами действуют те же принципы минимальных прав и проверяемых действий; подробнее — в материале об инструментах и ограничениях MCP-сервера.

Пошаговая проверка на тестовом стенде
1. Составьте карту данных и действий
Перечислите формы входа и редактирования, комментарии, загрузки, импорт, API и интеграции. Для каждого маршрута запишите источник данных, способ авторизации, разрешённые роли, операции с БД и места последующего вывода. Добавьте менее очевидные поверхности: превью черновика, сообщения об ошибках, административные списки и фоновые задания.
2. Проверьте границы изоляции
На отдельной тестовой среде с вымышленными данными убедитесь, что текст отображается именно как текст в нужных контекстах, разрешённая разметка проходит очистку, а опасные конструкции не попадают в итоговый документ. Проверьте ссылки и шаблоны разных типов. Для CSRF убедитесь, что отсутствующий или некорректный токен приводит к отказу, а действительный токен не обходит проверку доступа к объекту.
Для SQL-инъекций проведите ревизию мест построения запросов: параметры должны оставаться данными, а поля сортировки — выбираться только из заданного списка. Дополните ревизию автоматизированными тестами и проверкой зависимостей, подходящими вашему стеку. Тесты не доказывают отсутствие всех уязвимостей, но помогают не вернуть уже исправленную ошибку.
3. Зафиксируйте исправление и восстановление
Для каждой проблемы запишите затронутые версии и компоненты, условия воспроизведения на стенде, ответственного, исправление и проверку после него. Убедитесь, что исправление доступно и в расширениях, а настройки не потерялись после обновления. При необходимости привлеките специалиста к независимому аудиту. Проверки на чужих ресурсах проводятся только с разрешения владельца.
При подозрении на компрометацию сначала ограничьте доступ и сохраните необходимые данные для расследования, затем оцените масштаб, замените затронутые учётные данные и восстанавливайте доверенную версию по плану команды. Само восстановление из копии без устранения причины может вернуть ту же проблему.
Краткий чек-лист перед выпуском
- Недоверенный текст выводится безопасно в каждом используемом контексте; разрешённый HTML обрабатывает проверенный санитайзер.
- Изменяющие действия cookie-сессии защищены токенами и серверной проверкой прав; GET не выполняет изменение состояния.
- Значения в SQL передаются параметрами, а имена полей и направление сортировки выбираются из разрешённого списка.
- У базы, файлов и учётных записей только необходимые права; ошибки не раскрывают секреты посетителю.
- Обновления, расширения, резервные копии и восстановление имеют ответственных и проверяются на тестовой среде.
- Для ключевых исправлений есть повторяемые тесты, а подозрительные события доступны команде в защищённых журналах.
Начните с инвентаризации одной важной формы: проследите ввод до базы и обратно в браузер, проверьте авторизацию и CSRF-защиту изменения. Затем повторите процесс для остальных маршрутов и расширений. При разработке модулей используйте общие механизмы приложения — принципы жизненного цикла и разграничения возможностей рассмотрены в статье о разработке модуля для CMS.