ModSecurity
Общая информация
Сайт проекта: https://modsecurity.org/
Анализ логов удобнее делать через AuditConsole. Однако похоже проект сдох. Возможности:
- Централизация событий с помощью нескольких удаленных установок ModSecurity
- Хранение и извлечение событий
- Поддержка нескольких учетных записей пользователей и различных представлений
- Тегирование событий
- Правила событий, которые выполняются в консоли
Принципы modsecurity
- Гибкость: мощный язык правил.
- Пассивность: взаимодействие только по требованию
- Предсказуемость
- Качество выше количества
- Функция "очистка трафика" бессмысленна
Два варианта работы: встроенный и reverse proxy. Первый вариант логичнее для односерверных проектов. Второй желательно запускать в режиме кластера прокси, но упрощает процесс управления.
Компоненты
Парсер | Форматы данных используются анализаторами, которые извлекают фрагменты данных и сохраняют их для использования в правилах. |
Буферизация | При обычной установке буферизируется запрос и ответ. Важная функция, поскольку это единственный способ обеспечить надежную блокировку. Требует дополнительной ОП. |
Логгирование | Может сохранять весь HTTP-трафик. |
Механизм правил | К началу работы механизма правил, все необходимые фрагменты данных подготовлены. Правила оценивают транзакцию и предпринимают действия. |
Жизненный цикл транзакции. Транзакция последовательно проходит 5 этапов:
Заголовки запроса | Точка старта. Предоставляет создателям правил возможность быстрого первичного анализа запроса перед анализом тела запроса. Можно настроить, как будет анализироваться тело запроса. |
Тело запроса | Основные правила |
Заголовки ответа | Анализ происходит до получения тела ответа |
Тело ответа | |
Логгирование | Единственная фаза, в которой нельзя блокировать. Правила определяют, как и что нужно логгировать. |
Пример запроса/ответа и связи с жизненным циклом транзакции. Простой запрос и ответ, правил нет.
POST /?a=test HTTP/1.0 Content-Type: application/x-www-form-urlencoded Content-Length: 6 b=test |
HTTP/1.1 200 OK Date: Sun, 17 Jan 2010 00:13:44 GMT Server: Apache Content-Length: 12 Connection: close Content-Type: text/html Hello World! |
Процесс в контексте логов (похоже логи добавляются сверху):
Заголовки запроса |
[4] Initialising transaction (txid SopXW38EAAE9YbLQ). Был создан контекст, добавлены аргументы и проинициализирована транзакция. |
Тело запроса |
[4] Second phase starting (dcfg 8121800). Затем добавляются хуки фильтров [4] Hook insert_filter: Adding input forwarding filter (r 81d0588). После этого: [4] Input filter: Forwarding input: mode=0, block=0, nbytes=8192 (f 81d2228, r 81d0588). |
Заголовки ответа | [9] Output filter: Receiving output (f 81d2258, r 81d0588). [4] Starting phase RESPONSE_HEADERS. |
Тело ответа |
[9] Output filter: Bucket type MMAP contains 12 bytes. Затем ответ отправляется клиенту [4] Output filter: Output forwarding complete. |
Логгирование | [4] Initialising logging. [4] Starting phase LOGGING. [4] Audit log: Ignoring a non-relevant request. |
В случае загрузки файлов на сервер, файл буферизируется и затем удаляется. Маленькие файлы сохраняются в ОП, при превышении записываются на диск. Важно при настройке.
Запуск Docker
Оказалось несколько сложнее. ИИ не дает полного ответа, нужно отстраивать по частям. Есть разные образы nginx-modsecurity.
Структура проекта:
nginx-modsecurity/
├── Dockerfile
├── docker-compose.yml
├── nginx.conf
├── modsecurity.conf
├── nginx-logs/
├── modsec-logs/
└── html/
└── index.html
Dockerfile
# ---------- СТАДИЯ 1: Сборка ModSecurity и nginx ----------
FROM ubuntu:22.04 AS builder
ENV DEBIAN_FRONTEND=noninteractive
ENV NGINX_VERSION=1.26.2
WORKDIR /opt
# Устанавливаем зависимости
RUN apt-get update && apt-get install -y \
build-essential git automake autoconf libtool pkg-config \
libxml2 libxml2-dev libyajl-dev libssl-dev zlib1g zlib1g-dev \
libgeoip-dev liblmdb-dev libpcre2-dev \
wget curl ca-certificates && \
rm -rf /var/lib/apt/lists/*
# ---------- Сборка libmodsecurity ----------
RUN git clone --depth 1 -b v3/master https://github.com/SpiderLabs/ModSecurity && \
cd ModSecurity && \
git submodule init && git submodule update && \
./build.sh && ./configure && make -j$(nproc) && make install
# ---------- Сборка nginx + ModSecurity connector ----------
RUN wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
tar xzf nginx-${NGINX_VERSION}.tar.gz && \
git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git && \
cd nginx-${NGINX_VERSION} && \
./configure --prefix=/usr/local/nginx \
--with-compat \
--add-dynamic-module=../ModSecurity-nginx && \
make modules && make -j$(nproc) && make install
# ---------- СТАДИЯ 2: Финальный образ ----------
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
ENV NGINX_VERSION=1.26.2
#WORKDIR /etc/nginx
RUN apt-get update && apt-get install -y \
libyajl2 libxml2 libgeoip1 liblmdb0 ca-certificates && \
rm -rf /var/lib/apt/lists/*
# Копируем всё нужное из builder
COPY --from=builder /usr/local/modsecurity/ /usr/local/modsecurity/
COPY --from=builder /usr/local/nginx /usr/local/nginx
COPY --from=builder /opt/nginx-${NGINX_VERSION}/objs/ngx_http_modsecurity_module.so /usr/local/nginx/modules/
ENV PATH="/usr/local/nginx/sbin:${PATH}"
# Создаем необходимые директории
RUN mkdir -p /var/log/modsecurity \
&& mkdir -p /var/log/nginx \
&& mkdir -p /etc/modsecurity.d \
&& mkdir -p /usr/local/nginx/conf
# Копируем конфигурационные файлы, все равно потом перезапишем в compose
COPY nginx.conf /usr/local/nginx/conf/nginx.conf
COPY modsecurity.conf /etc/modsecurity.d/modsecurity.conf
COPY html /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
docker-compose.yml
services:
nginx-modsecurity:
build: .
container_name: nginx-modsecurity-ubnt
ports:
- "80:80"
volumes:
- ./html:/usr/local/nginx/html
- ./nginx.conf:/usr/local/nginx/conf/nginx.conf
- ./nginx-logs:/usr/local/nginx/logs
- ./modsec-logs:/var/log/modsecurity
restart: unless-stopped
nginx.conf
load_module /usr/local/nginx/modules/ngx_http_modsecurity_module.so;
user www-data;
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /usr/local/nginx/conf/mime.types;
default_type application/octet-stream;
# Директивы для логов (должны быть ДО server блоков)
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
modsecurity on;
modsecurity_rules_file /etc/modsecurity.d/modsecurity.conf;
server {
listen 80;
server_name _;
location / {
root /usr/share/nginx/html;
index index.html;
modsecurity on;
}
location /status {
access_log off;
return 200 "Nginx on Ubuntu is working!\n";
add_header Content-Type text/plain;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
root /usr/share/nginx/html;
modsecurity off;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
}
index.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Nginx + ModSecurity</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
</style>
</head>
<body>
<h1>Nginx работает!</h1>
<div>
<h3>Тестовые ссылки:</h3>
<ul>
<li><a href="/">Нормальный запрос</a></li>
<li><a href="/?test=malicious">Тестовая блокировка (сработает правило 1001)</a></li>
<li><a href="/status">Статус страница</a></li>
</ul>
</div>
</body>
</html>
Запуск и тестирование
# Запустите контейнер
docker compose up --build -d
# Проверьте логи
docker compose logs -f
# Тестовые запросы
curl http://localhost/
curl "http://localhost/?test=<script>alert('xss')</script>"
modsecurity.conf в разделе Настройка modescurity
Структура файла настроек
Файл настройки разбит на следующие блоки (логически)
- Общие настройки
- Настройки аудита
- Дополнительные настройки
- Правила
Можно распределить настройки по файлам, указав в основном файле ссылки на дополнительные файлы.
Общие настройки
Параметр | Описание | Необходимость |
Основной | ||
SecRuleEngine | Включает модуль
DetectionOnly - только логгирование (но лимиты все равно работают) On - включена и блокировка
|
+ |
SecDefaultAction | Действие по умолчанию в случае соответствия фильтрам.
|
+/- |
Настройки запроса | ||
SecRequestBodyAccess | Буферизация тела запроса. Если отключить, то не будет доступа к post параметрам.
|
- |
SecRequestBodyLimit | Максимальный размер тела запроса. Устаревший параметр.
|
- |
SecRequestBodyLimitAction |
Действие при превышении лимита тела запроса ProcessPartial продолжить без буферизации Reject запретить |
- |
SecRequestBodyInMemoryLimit | Максимальный размер тела запроса (не понял отличия) Возможно, лимит буферизации в ОП или на диске | - |
SecRequestBodyNoFilesLimit | Максимальный размер тела запроса исключая размер файла | - |
Настройки ответа | ||
SecResponseBodyAccess | Буферизация тела ответа. Часто можно установить в Off, поскольку известны ответы.
|
|
SecResponseBodyLimit SecResponseBodyLimitAction |
Как для запроса | |
SecResponseBodyMimeType |
Список MIME типов для анализа
|
|
SecResponseBodyMimeTypesClear |
ХЗ | |
Хранение данных | ||
SecDataDir | Папка хранения постоянных данных
|
+ |
SecTmpDir | Папка хранения временных данных
|
+ |
SecUploadDir | Размещение закачанных файлов | |
SecUploadKeepFiles | Включить / выключить сохранение файлов | |
SecUploadFileMode | Права на сохраненные файлы
|
|
SecUploadFileLimit | Ограничение на количество файлов в одном запросе |
Настройки логгирования
Параметр | Описание | Необходимость |
Лог отладки | ||
SecDebugLog | Файл сохранения лога отладки | + |
SecDebugLogLevel | Уровень предупреждений. Хватает 3. 0-отключено, 9-максимально | - |
Лог данных | ||
SecAuditEngine |
On логгировать все RelevantOnly только попавшие под правила Off не логгировать |
+ |
SecAuditLog |
Файл лога |
+ |
SecAuditLogRelevantStatus |
Статус ответа, попадающего под лог. Например
|
- |
SecAuditLogParts |
A Заголовок события (время, уникальный ID и т.д.)
|
- |
SecAuditLogType |
Serial Все логи в один файл — последовательно (по умолчанию). |
- |
Дополнительные настройки
Параметр | Описание |
SecArgumentSeparator | Устанавливает разделитель в application/x-www-form-urlencoded По умолчанию & но иногда бывает и другое
|
SecCookieFormat | Версия парсера cookies. 0 или 1. 0 - по умолчанию - более чем достаточно. |
Пример минимального файла без правил.
# Basic Configuration
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess Off
# Audit Log Settings
SecAuditEngine On
SecAuditLogType Serial
SecAuditLog /var/log/modsecurity/audit.log
# Debug Settings
SecDebugLog /var/log/modsecurity/debug.log
SecDebugLogLevel 9
# Temporary directories
SecTmpDir /tmp/
SecDataDir /tmp/
Настройка отладки
Для nginx нет возможности разделить настройку в стиле тегов в пределах файла конфигурации modsecurity. Можно разделить только на уровне web сервера.
location /myapp/ {
modsecurity on;
modsecurity_rules_file /usr/local/nginx/modsecurity/modsecurity_debug.conf;
root /usr/local/nginx/html;
}
То есть это отдельный файл с собственными настройками. Вариант динамического изменения уровня в правилах тоже не работает в 3 версии.
Возможен анализ User-Agent
SecRule REQUEST_HEADERS:User-Agent YOUR_UNIQUE_ID phase:1,nolog,pass,ctl:debugLogLevel=9
Правила modsecurity
SecRule VARIABLES OPERATOR TRANSFORMATION
VARIABLES | Точки запроса, к которым применяется правило. |
OPERATOR | Регулярное выражение для поиска в VARIABLES. |
TRANSFORMATION | Действия в случае совпадения |
Переменные (VARIABLES)
- REQUEST_URI: URI запроса;
- ARGS: параметры запроса;
- REQUEST_BODY: тело запроса;
- REQUEST_COOKIES - куки;
- REQUEST_HEADERS - заголовки;
- XML - XML код в запросе.и т.д.
Операторы (OPERATOR)
- @contains: содержит указанное значение;
- @rx: совпадает с регулярным выражением;
- @eq: равно указанному значению и другие.
Модификаторы (TRANSFORMATION)
SecRule ARGS "password" "@rx (?i)password" "id:2,phase:2,t:none,t:lowercase,t:urldecode,t:replaceComments"
- t:none: Отключить все стандартные преобразования;
- t:lowercase: Преобразовать в нижний регистр;
- t:urldecode: Декодировать URL;
- t:replaceComments: Удалить комментарии.
Дополнительные опции
SecRule REQUEST_METHOD "@streq POST" "id:3,phase:2,chain,t:none,pass,nolog"
SecRule REQUEST_URI "@rx /login" "t:none,t:urldecode,t:replaceComments"
- chain: Связывает два правила так, что следующее применяется только если предыдущее сработало;
- pass: Продолжить выполнение правил вне зависимости от результата текущего;
- nolog: Не записывать событие в лог.
Примеры правил ModSecurity
Пример 1:
SecRule ARGS "password" "@rx (?i)password" "id:123,deny,status:403,msg:'Password leakage detected'"
ARGS: обращение к параметрам запроса
@rx: оператор совпадения с регулярным выражением
id: идентификатор правила
deny: действие с запросом - запретить
status: HTTP-статус ответа при срабатывании правила - 403
msg: название правила - 'Password leakage detected'
Данное правило ищет в параметрах запроса "password" и срабатывает при его обнаружении.
Пример 2:
SecRule ARGS "@rx ^(?:\'|\"|%27|%22|%3E|%3C|%3D|%3B|%20or%20|union|select|insert|update|delete|drop)" \
"id:1,phase:2,deny,msg:'SQL Injection Attack'"
Правило ищет SQL-инъекции в параметрах запроса (ARGS) и блокирует запрос, если обнаруживается соответствие с паттерном.
Пример 3:
SecRule REQUEST_COOKIES|REQUEST_HEADERS|ARGS|XML:/* \
"(<|%3C)([^sS]*s[sS]*c[cC]*r[rR]*i[iI]*p[pP]*t[tT]*)(>|%3E)" \
"id:2,phase:2,t:none,deny,msg:'Cross-site Scripting (XSS) Attack'"
Правило обнаруживает попытки вставки скриптов в запросы (XSS) и блокирует их.
Пример 4:
SecRule FILES_TMPNAMES "@inspectFile /etc/passwd" \
"id:3,phase:2,t:none,deny,msg:'Attempt to access /etc/passwd'"
Правило анализирует временные файлы, загруженные на сервер, и блокирует запрос, если в содержимом обнаруживается попытка доступа к файлу /etc/passwd.
Пример 5:
SecRule RESPONSE_BODY "@contains 'Invalid password'" \
"id:5,phase:4,t:none,log,deny,msg:'Password brute force attempt'"
Правило обнаруживает попытки подбора пароля по сообщению об ошибке в теле ответа и блокирует соответствующий запрос.
Как писать правила ModSecurity
ModSecurity Core Rule Set (CRS) — это набор правил, разработанный сообществом Open Web Application Security Project (OWASP) для использования с ModSecurity. Эти правила предназначены для обеспечения базовой защиты от различных видов популярных веб-атак. Вам не всегда нужно создавать свои собственные правила, так как CRS уже предоставляет обширный набор правил, охватывающих множество сценариев атак.
Но если у организации есть специфические требования или вы хотите настроить правила под свои нужды, вы можете создавать собственные правила, в соответствии со следующим алгоритмом:
1. Определите цель правила
Определите, какую атаку или вид активности вы хотите обнаруживать.
Решите, на какую часть HTTP-запроса (например, заголовки, тело запроса, параметры запроса) должно применяться правило.
2. Создайте правило
Определите фазу, на которой будет выполняться правило (например, phase:1 или phase:2).
Используйте документацию ModSecurity для создания правила, например:
SecRule REQUEST_HEADERS:User-Agent "@contains badbot" "id:1001,phase:2,deny,msg:'Bad Bot Detected.'"
Это правило запрещает запросы с User-Agent, содержащим строку "badbot" в заголовках.
3. Тестируйте правило
Включите правило и протестируйте его - нужно убедиться, что правило срабатывает на атаки и не фолсит.
Используйте инструменты для тестирования веб-безопасности (например, burp suit), чтобы проверить, как правило реагирует на различные виды запросов.
Если правило срабатывает не так, как хотелось бы, или блокирует легитимные запросы, исправьте его, чтобы снизить ложные срабатывания.
Просматривайте логи ModSecurity для выявления проблем и улучшения правил.
4. Документируйте
Добавьте комментарии к правилу, объясняющие его цель и предназначение.
Ведите документацию, чтобы другие коллеги могли легко понять, почему правило было создано.
Другой пример правила, предназначенного для блокировки SQL-инъекций:
SecRule ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/* "@rx (?i:(?:\b(?:union\s*all|select\s*(?:.+)\s*from|create\s*(?:.+)\s*table|delete\s*from|drop\s*(?:.+)\s*table|exec\s*(?:\w+\s*|\s*)\(|insert\s*into|shutdown|update\s*(?:.+)\s*set)\b))" "phase:2,deny,status:403,id:1002,msg:'SQL Injection attempt.'"
Это правило проверяет параметры запроса, заголовки и XML-данные на наличие попыток SQL-инъекций.
No comments to display
No comments to display