Решение ложных срабатываний ModSecurity для CMS: практическое руководство на примере jedig.ru

Введение

ModSecurity с правилами OWASP CRS (Core Rule Set) — это мощный инструмент защиты веб-приложений от атак. Однако при работе с современными CMS, такими как Joomla с компонентом VirtueMart, WordPress с визуальными редакторами или другими системами, администраторы часто сталкиваются с проблемой ложных срабатываний. Легитимный HTML-код, примеры команд и техническая документация, которые пользователи вводят в поля описания товаров, статей или комментариев, воспринимаются WAF как XSS-атака, попытка выполнения команд или SQL-инъекция.

В этой статье мы разберем две реальные ситуации, с которыми столкнулся сайт jedig.ru: блокировку HTML-описаний товаров в VirtueMart и блокировку технических статей в Joomla. Мы покажем правильные способы решения этих проблем без потери безопасности.

Проблема №1: Блокировка описаний товаров VirtueMart

На сайте jedig.ru, работающем под управлением Joomla с компонентом VirtueMart, при попытке сохранить товар с форматированным описанием (использующим HTML-теги для создания табов, списков, стилизации) ModSecurity блокировал запрос с ошибкой 403 Forbidden.

Что происходило

Администратор заполнял поле product_desc (описание товара) HTML-кодом следующего вида:

<ul class="nav nav-tabs" role="tablist">
  <li class="active">
    <a href="#review" role="tab" data-toggle="tab">📖 Обзор</a>
  </li>
  <li>
    <a href="#average" role="tab" data-toggle="tab">⭐ Отзывы</a>
  </li>
</ul>
<div class="tab-content">
  <div class="tab-pane active" id="review">
    <p>Описание товара...</p>
  </div>
</div>

При нажатии кнопки "Сохранить" сервер возвращал ошибку 403, и изменения не применялись.

Анализ логов ModSecurity

Открыв файл modsec_audit.log, мы обнаружили следующие записи о блокировке:

ModSecurity: Warning. Matched "Operator `Rx'" against variable `ARGS:product_desc'
[file "/etc/modsecurity/owasp/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf"]
[id "932130"]
[msg "Remote Command Execution: Unix Shell Expression Found"]

ModSecurity: Warning. detected XSS using libinjection.
[file "/etc/modsecurity/owasp/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf"]
[id "941100"]
[msg "XSS Attack Detected via libinjection"]

ModSecurity: Warning. [id "941160"]
[msg "NoScript XSS InjectionChecker: HTML Injection"]

ModSecurity: Warning. [id "941310"]
[msg "US-ASCII Malformed Encoding XSS Filter - Attack Detected"]

ModSecurity: Access denied with code 403 (phase 2).
[id "949110"]
[msg "Inbound Anomaly Score Exceeded (Total Score: 20)"]

Что означают эти записи

  1. Правило 932130 (RCE) сработало на символы > и ( в HTML-коде, которые оно интерпретировало как попытку выполнения shell-команды.
  2. Правило 941100 (XSS libinjection) обнаружило HTML-теги <ul>, <a> и атрибуты, которые библиотека libinjection классифицировала как XSS-атаку.
  3. Правило 941160 (XSS NoScript) среагировало на атрибуты style=, href= и другие HTML-атрибуты.
  4. Правило 941310 (US-ASCII Malformed Encoding) сработало на кириллические символы в UTF-8 кодировке, которые после определенных трансформаций выглядели как подозрительные последовательности байтов.
  5. Правило 949110 (Anomaly Score) суммировало "штрафные баллы" от всех предыдущих правил и, так как общий счет превысил пороговое значение (20 > 5), заблокировало запрос.

Проблема №2: Блокировка технических статей в Joomla

Вторая проблема возникла при попытке опубликовать на jedig.ru техническую статью о настройке ModSecurity. В тексте статьи приводились примеры кода, команды для терминала, пути к файлам и даже примеры вредоносных конструкций (для иллюстрации).

Что происходило

Автор заполнял основное поле текста статьи в Joomla (jform[articletext]) HTML-разметкой, содержащей:

  • Примеры HTML-кода с тегами <script> и функциями alert('XSS')
  • Примеры команд терминала: sudo nano /etc/modsecurity/...
  • Примеры SQL-запросов и путей к системным файлам
  • Примеры конфигурации ModSecurity с директивами SecRule

При нажатии кнопки "Сохранить" сервер снова возвращал ошибку 403 Forbidden.

Анализ логов ModSecurity

В этот раз лог показал ещё более разнообразную картину:

ModSecurity: Warning. [id "930120"]
[msg "OS File Access Attempt"]
[data "Matched Data: etc/modsecurity found within ARGS:jform[articletext]"]

ModSecurity: Warning. [id "932235"]
[msg "Remote Command Execution: Unix Command Injection"]

ModSecurity: Warning. [id "932130"]
[msg "Remote Command Execution: Unix Shell Expression Found"]

ModSecurity: Warning. [id "932260"]
[msg "Remote Command Execution: Direct Unix Command Execution"]

ModSecurity: Warning. [id "941100"]
[msg "XSS Attack Detected via libinjection"]

ModSecurity: Warning. [id "941110"]
[msg "XSS Filter - Category 1: Script Tag Vector"]

ModSecurity: Warning. [id "941160"]
[msg "NoScript XSS InjectionChecker: HTML Injection"]

ModSecurity: Warning. [id "941390"]
[msg "Javascript method detected"]

ModSecurity: Warning. [id "942190"]
[msg "Detects MSSQL code execution and information gathering attempts"]

ModSecurity: Access denied with code 403 (phase 2).
[id "949110"]
[msg "Inbound Anomaly Score Exceeded (Total Score: 45)"]

Что означают эти записи

  1. 930120 (LFI) — обнаружил путь /etc/modsecurity в тексте статьи и принял его за попытку доступа к системным файлам.
  2. 932235, 932130, 932260 (RCE) — нашли примеры Unix-команд sudo, nano, shell-выражения и восприняли их как инъекцию команд.
  3. 941100, 941110, 941160, 941390 (XSS) — обнаружили теги <script>, функцию alert() и другие JavaScript-конструкции из раздела "Проверка результата".
  4. 942190 (SQLi) — нашёл подозрительные символы и SQL-паттерны в примерах кода.
  5. 949110 — суммарный штрафной балл достиг 45, запрос заблокирован.

Объединяющая причина обеих проблем

Обе проблемы имеют одну и ту же природу: OWASP CRS не знает контекста. Он видит паттерны, похожие на атаки, но не может отличить:

  • Реальную XSS-атаку от примера HTML-кода в описании товара
  • Попытку LFI-атаки от упоминания пути к файлу в технической статье
  • Инъекцию команд от примера команды для терминала в обучающем материале

Разработчики OWASP CRS предусмотрели специальный механизм для решения таких ситуаций — исключения по переменным.

Неправильные подходы к решению

Подход 1: Полное отключение правил для всей админки

Некоторые пытаются решить проблему, отключая правила для всего URI админки:

SecRule REQUEST_URI "@beginsWith /administrator/index.php" \
    "id:9001338,\
    phase:1,\
    pass,\
    nolog,\
    ctl:ruleRemoveById=941310"

Почему это не работает:

  • Правило срабатывает в phase:1, когда тело запроса еще не обработано
  • Отключается только одно правило (941310), но блокируют запрос другие правила
  • Снижается безопасность всей админки

Подход 2: Отключение всех правил XSS/RCE

Другие пытаются отключить все правила защиты от XSS и RCE:

SecRuleRemoveByTag "attack-xss"
SecRuleRemoveByTag "attack-rce"

Почему это опасно:

  • Полностью отключается защита от реальных XSS-атак
  • Сайт становится уязвимым для инъекций вредоносного кода
  • Это нарушение базовых принципов безопасности

Подход 3: Использование chain-правил с ctl:ruleRemoveById

SecRule REQUEST_URI "@rx ^/administrator/index\.php" \
    "id:9001338,\
    phase:2,\
    pass,\
    nolog,\
    chain"
    SecRule ARGS_NAMES "@streq product_desc" \
        "ctl:ruleRemoveById=932130,..."

Почему это работает, но не идеально:

  • Требует точного указания phase:2phase:1 тело запроса недоступно)
  • Сложнее для чтения и поддержки
  • Менее предпочтительно, чем встроенный механизм исключений

Правильное решение: SecRuleUpdateTargetById и SecRuleUpdateTargetByTag

OWASP CRS предоставляет два мощных механизма для точечного исключения конкретных полей из проверки:

  • SecRuleUpdateTargetById — исключает поле из проверки конкретным правилом по ID
  • SecRuleUpdateTargetByTag — исключает поле из проверки всей группы правил по тегу

Шаг 1: Открытие файла исключений

Откройте файл локальных исключений CRS. Обычно это:

sudo nano /etc/modsecurity/owasp/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf

Шаг 2: Добавление правил исключения для VirtueMart

Для поля product_desc (описание товара) добавляем точечные исключения по ID правил:

# Исключения для поля product_desc (описание товара VirtueMart)
# Отключаем правила, которые ложно срабатывают на HTML-разметку в админке
SecRuleUpdateTargetById 932130 "!ARGS:product_desc"
SecRuleUpdateTargetById 941100 "!ARGS:product_desc"
SecRuleUpdateTargetById 941160 "!ARGS:product_desc"
SecRuleUpdateTargetById 941310 "!ARGS:product_desc"

Шаг 3: Добавление правил исключения для статей Joomla

Для поля jform[articletext] (основной текст статьи) используем исключения по тегам, чтобы охватить все возможные типы атак, которые могут встретиться в технических статьях:

# Исключения для основного текста статей Joomla (jform[articletext])
# Поле редактируется только авторизованными авторами/администраторами.
# В статьях IT-блога встречаются примеры кода, которые не являются атаками.
SecRuleUpdateTargetByTag "attack-xss" "!ARGS:jform[articletext]"
SecRuleUpdateTargetByTag "attack-rce" "!ARGS:jform[articletext]"
SecRuleUpdateTargetByTag "attack-sqli" "!ARGS:jform[articletext]"
SecRuleUpdateTargetByTag "attack-lfi" "!ARGS:jform[articletext]"

Шаг 4: Перезагрузка веб-сервера

Примените изменения:

sudo systemctl reload apache2
# или
sudo systemctl reload nginx

Как это работает

Директива SecRuleUpdateTargetById говорит ModSecurity:

"Когда будешь проверять правило с указанным ID, НЕ проверяй в нём указанную переменную"

Директива SecRuleUpdateTargetByTag говорит:

"Когда будешь проверять ВСЕ правила с указанным тегом, НЕ проверяй в них указанную переменную"

В нашем случае:

  • "!ARGS:product_desc" и "!ARGS:jform[articletext]" — восклицательный знак означает "ИСКЛЮЧИТЬ"
  • "attack-xss", "attack-rce" и т.д. — это теги, которыми OWASP CRS помечает группы правил

Почему это безопасно

1. Точечное исключение

Мы исключаем только конкретные поля (product_desc и jform[articletext]) из проверки определёнными правилами. Все остальные поля формы и все остальные правила продолжают работать.

2. Контекст использования

Поля product_desc и jform[articletext] заполняются только в админке сайта авторизованными пользователями (администраторами, редакторами, авторами). Доступ к админке защищен логином и паролем. HTML-код и примеры команд в этих полях — это легитимный контент.

3. Защита других полей

Если злоумышленник попытается внедрить XSS-код в другие поля (например, в параметр поиска search, имя пользователя username, заголовок статьи jform[title] или любой другой параметр), ModSecurity продолжит проверять эти поля и заблокирует атаку.

4. Соответствие best practices

Использование SecRuleUpdateTargetById и SecRuleUpdateTargetByTag — это официальный рекомендуемый способ решения ложных срабатываний в документации OWASP CRS.

Проверка результата

После применения решения:

  1. Попробуйте сохранить товар с HTML-описанием в админке VirtueMart
  2. Попробуйте сохранить техническую статью с примерами кода в редакторе статей Joomla
  3. Проверьте modsec_audit.log — записей о блокировке для полей product_desc и jform[articletext] больше не должно быть
  4. Убедитесь, что другие поля продолжают проверяться (попробуйте ввести <script>alert('XSS')</script> в поле поиска или в заголовок статьи — оно должно быть заблокировано)

Дополнительные рекомендации

1. Документируйте исключения

Всегда добавляйте комментарии к правилам исключения, объясняя:

  • Почему это исключение необходимо
  • Какое поле исключается
  • Какие правила отключаются
  • Кто добавил исключение и когда

2. Регулярно пересматривайте исключения

При обновлении OWASP CRS или изменении функционала сайта пересматривайте список исключений. Возможно, некоторые из них больше не нужны.

3. Используйте детальный логинг для отладки

Временно включите детальное логирование для отладки:

SecRuleEngine DetectionOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABCDEFHZ
SecAuditLogType Serial
SecAuditLog /var/log/modsec_audit.log

После настройки исключений верните SecRuleEngine On для включения блокировки.

4. Выбирайте между ById и ByTag

Используйте SecRuleUpdateTargetById, когда:

  • Нужно отключить только конкретные правила
  • Важна максимальная точность
  • Известны точные ID сработавших правил

Используйте SecRuleUpdateTargetByTag, когда:

  • Нужно отключить всю группу правил (например, все XSS-правила)
  • Поле может срабатывать на множество разных правил
  • Хотите избежать проблем при обновлении CRS (новые правила с тем же тегом тоже будут исключены)

5. Пример полного файла исключений

Вот как может выглядеть итоговый файл REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf для jedig.ru:

# ============================================
# Локальные исключения для jedig.ru
# Обновлено: 13.06.2026
# ============================================

# --- VirtueMart: описание товара ---
# Поле содержит HTML-разметку (табы, списки, стили)
SecRuleUpdateTargetById 932130 "!ARGS:product_desc"
SecRuleUpdateTargetById 941100 "!ARGS:product_desc"
SecRuleUpdateTargetById 941160 "!ARGS:product_desc"
SecRuleUpdateTargetById 941310 "!ARGS:product_desc"

# --- Joomla: основной текст статей ---
# IT-блог содержит примеры кода, команд, путей к файлам
SecRuleUpdateTargetByTag "attack-xss" "!ARGS:jform[articletext]"
SecRuleUpdateTargetByTag "attack-rce" "!ARGS:jform[articletext]"
SecRuleUpdateTargetByTag "attack-sqli" "!ARGS:jform[articletext]"
SecRuleUpdateTargetByTag "attack-lfi" "!ARGS:jform[articletext]"

Заключение

Ложные срабатывания ModSecurity при работе с CMS, использующими HTML-контент и техническую документацию — это распространенная проблема, которая имеет элегантное решение. Использование директив SecRuleUpdateTargetById и SecRuleUpdateTargetByTag позволяет точечно исключить конкретные поля из проверки определёнными правилами или группами правил, сохраняя при этом высокий уровень безопасности для всех остальных полей и векторов атак.

В случае с jedig.ru мы смогли решить обе проблемы:

  • Блокировку HTML-описаний товаров в VirtueMart — добавив 4 строки с исключениями по ID
  • Блокировку технических статей в Joomla — добавив 4 строки с исключениями по тегам

При этом защита от реальных XSS-атак, SQL-инъекций, RCE и LFI для всех остальных полей осталась полностью функциональной.

Помните: правильная настройка WAF — это баланс между безопасностью и удобством использования. Не отключайте защиту полностью — настраивайте её точечно под ваши конкретные задачи.

Полезные ссылки