Здравствуйте!
В данной статье мы рассмотрим пример реализации шлюза вэб фильтрации на базе ПО с открытым исходным кодом (стек Apache/ModSecurity/OWASP CRS/ProjectHoneyPot).
Посмотрим как это все объединить в одно целое. Где взять и как подгрузить сигнатуры. Как отладить (дефолтные конфигурации — не наш метод). В процессе напишем пару дополнительных правил, наступим на грабли, найдем что‑то похожее на изящный выход из ситуации. Узнаем как собирать статистику о ботах и как от них защититься.
В мой изначальный план так же входили Snort и защита от распределенных атак, но заглянув в мануал снорта стало понятно, что там дело не одной и думаю не пары недель (тем более очень хотелось бы разобраться с кастомными сигнатурами). Так что можете рассматривать эту статью как первую в своем цикле.
Штош,приступим!
UPDATE
Вижу, что в карму сыпятся минусы - очевидно что-то идет не так (есть некая вероятность, что это будет последняя публикация)
Поэтому позволю себе добавить опрос на тему "А нужен ли здесь такого рода контент ?" Буду благодарен если вы поможете во всем разобраться.
Спасибо
Введение
Для начала отвечу на один из главных вопросов — почему OpenSource? — Все просто, можно взять F5, Imperva, есть решения и у Fortinet, и у Check Point, и у Palo. Конечно же есть решения Российских вендоров. Взять‑то конечно можно, но вот доступ к таким технологиям имеет лишь не самая большая аудитория. По этой причине рассматриваем OpenSource
Очень актуальный сегодня вопрос согласования с ИБ оставим за скобками. Риски, как ни крути присутствуют и я бы 10 раз подумал (и отказался) прежде чем реализовывать нечто подобное для защиты критической инфраструктуры.
В то же время, данное решение может подойти малому бизнесу и энтузиастам в ИТ. Ведь сколько у нас приложений опубликовано в сеть с минимальной фильтрацией? А сколько из них обновляется?
И так, что же такое WAF?
В первую очередь — фаерволл, с функциями несколько отличающимися от типичного NGFW. Шлюзы вэб фильтрации, как несложно было бы догадаться, призваны защищать вэб приложения потенциально уязвимые к внешним атакам.
Вендорские решения охватывают несколько больший функционал. Как правило это:
Анализ трафика приложения облегчающий составление политик фильтрации
Фильтрация OWASP сигнатурами (защита от XSS, SQL инджектов, анализ HTTP заголовков, защита от известных уязвимостей нулевого дня и так далее)
Защита от ботов
Некоторые вендоры в довесок предоставляют функционал NGFW. Здесь вам и песочница, и IPS, и защита от вредоносного ПО. Все стандартно.
Так же встречаются WAF с функцией балансировщика
Что из этого мы можем реализовать? — Многое. Придется ли при этом страдать изобретая велосипед? — Несомненно
Анализ, репорты? Только своими руками. Можно (и даже нужно) конечно логи перенаправлять в SIEM именно с этой целью
OWASP? Ну, да. Никаких вам графических интерфейсов и только хардкор
Детекция приложений? Теоретически тоже да. Здесь придется писать собственные сигнатуры
Защита от ботов? В какой‑то мере.
Понятно конечно, что вендор и с сапортом поможет, и связан юридическими обязательствами, и потенциальный бэкдор ему в репозиторий сложнее залить, да и функционал как правило шире. Все это понятно.
Но в общеобразовательных целях мы будем рассматривать OpenSource
ModSecurity
ModSecurity есть ничто иное как кросс‑платформенный модуль шлюза вэб фильтрации для вэб сервера. Поддерживается как Apache, так и Nginx. Может выполнять фильтрацию своими силами, а может использовать сигнатуры OWASP CRS
Установка здесь тривиальная и не займет много времени. Предположу что у вас уже есть Linux дистрибутив и вэб сервер (здесь и далее рассматриваем Debian + Apache)
Устанавливаем пакет
apt install libapache2-mod-security2
Включаем модуль
a2enmod security2
Подгружаем рекомендованный файл конфигурации
mv /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
Проверяем режим работы движка (Оставляем в DetectionOnly. У нас будет адаптивная блокировка)
grep SecRuleEngine /etc/modsecurity/modsecurity.conf
Проверяем включен ли скан ответов сервера
grep SecResponseBodyAccess /etc/modsecurity/modsecurity.conf
Проверяем все ли в порядке с конфигом модуля
cat /etc/apache2/mods-enabled/security2.conf
Перезагружаем Apache
systemctl restart apache2
systemctl status apache2
В принципе какой‑то дополнительной настройки можно и избежать
OWASP CRS
Что же это такое? Не более чем общий набор статических правил оценки и фильтрации трафика для ModSecurity
Установка
Подгружаем правила
cd /var/tmp
wget https://github.com/coreruleset/coreruleset/archive/refs/tags/v4.10.0.tar.gz
wget https://github.com/coreruleset/coreruleset/releases/download/v4.10.0/coreruleset-4.10.0.tar.gz.asc
Проверяем целостность
gpg --fetch-key https://coreruleset.org/security.asc
gpg --edit-key <RSA KEY ID>
gpg> trust
Your decision: 5 (ultimate trust)
Are you sure: Yes
gpg> quit
gpg --verify coreruleset-4.10.0.tar.gz.asc v4.10.0.tar.gz
Устанавливаем правила
mkdir /etc/crs4
tar -xzvf v4.10.0.tar.gz --strip-components 1 -C /etc/crs4
Подгружаем файл конфигурации
cd /etc/crs4
mv crs-setup.conf.example crs-setup.conf
Подгружаем в ModSecuirty модуль
nano -w /etc/apache2/mods-enabled/security2.conf
# Include OWASP ModSecurity CRS rules if installed
#IncludeOptional /usr/share/modsecurity-crs/*.load
IncludeOptional /etc/crs4/crs-setup.conf
IncludeOptional /etc/crs4/plugins/*-config.conf
IncludeOptional /etc/crs4/plugins/*-before.conf
IncludeOptional /etc/crs4/rules/*.conf
IncludeOptional /etc/crs4/plugins/*-after.conf
Перезагружаем Apache
systemctl restart apache2
systemctl status apache2
Настройка
Разбираемся с конфигом. Конфиг, если его прочитать, сам ответит на все возникающие вопросы.
nano -w /etc/crs4/crs-setup.conf
Проверяем, что модуль работает в Anomaly Scoring режиме
SecDefaultAction "phase:1,log,auditlog,pass"
SecDefaultAction "phase:2,log,auditlog,pass"
Выставляем Paranoia Level = 3 (А почему бы и да? Секурность мы с вами повысим, а с ложными срабатываниями разберемся при необходимости) Кто бы знал, во что это выльется :-)
SecAction \
"id:900000,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.blocking_paranoia_level=3"
Принудительно включаем Body Processor URLENCODED для всех клиентских запросов с отсутствующим Content‑Type заголовком
SecAction \
"id:900010,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.enforce_bodyproc_urlencoded=1"
Задаем параметры (веса?) для аномалий
SecAction \
"id:900100,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.critical_anomaly_score=5,\
setvar:tx.error_anomaly_score=4,\
setvar:tx.warning_anomaly_score=3,\
setvar:tx.notice_anomaly_score=2"
Задаем пороговые значения для блокировки. Предлагаю задрать пока не отладим.
SecAction \
"id:900110,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.inbound_anomaly_score_threshold=5000,\
setvar:tx.outbound_anomaly_score_threshold=4000"
Включаем второй уровень логирования (Как‑никак у нас PL=3 и видеть что именно происходит было бы крайне полезным)
SecAction \
"id:900115,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.reporting_level=2"
Включаем дефолтный набор сигнатур
SecAction \
"id:900130,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.enable_default_collections=1"
Включаем ограничение HTTP методов. Я добавил PUT метод используемый CMS (Здесь и далее — осторожнее, ибо многое зависит от опубликованных приложений. Я писал под себя, по этому внимательно и без копипасты) Подробнее про логику здесь и здесь
SecAction \
"id:900200,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:'tx.allowed_methods=GET HEAD POST OPTIONS PUT'"
Ограничиваем HTTP Content Type
SecAction \
"id:900220,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:'tx.allowed_request_content_type=|application/x-www-form-urlencoded| |text/html|'"
Ограничиваем HTTP версии
SecAction \
"id:900230,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:'tx.allowed_http_versions=HTTP/1.1'"
Ограничиваем кодировку
SecAction \
"id:900280,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:'tx.allowed_request_content_type_charset=|utf-8|'"
Ограничиваем количество аргументов, длину имени аргумента и его собственную длину, суммарную длину всех аргументов.
SecAction \
"id:900300,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.max_num_args=255"
SecAction \
"id:900310,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.arg_name_length=100"
SecAction \
"id:900320,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.arg_length=400"
SecAction \
"id:900330,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.total_arg_length=64000"
Ограничиваем размер файла в POST запросе, а так же суммарный размер всех файлов
SecAction \
"id:900340,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.max_file_size=1048576"
SecAction \
"id:900350,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.combined_file_sizes=1048576"
Проверяем наш сервер на предмет поддержки кодировки
SecAction \
"id:900950,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.crs_validate_utf8_encoding=1"
Остальное оставляем по умолчанию
Включаем фильтрацию в конфигурации виртуального хоста
nano -w /etc/apache2/sites-enabled/null.conf
# WAF
<IfModule security2_module>
SecRuleEngine On
</IfModule>
Перезагружаем Apache
systemctl restart apache2
Отладка
Берем напильник и приступаем. В одной руке — терминал
tail -f /var/log/apache2/modsec_audit.log | grep -iE ".*notice.*|*.warning.*|.*error.*|.*critical.*"
Другой — начинаем проверять функционал приложения
Проблема 1
Message: Rule 7faba9cd8db8 [id "951250"][file "/etc/crs4/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf"][line "365"] - Execution error - PCRE limits exceeded (-47): (null).
Здесь каких‑то рекомендаций кроме как trial and error нет. Предлагаю сделать следующее (не забываем рестартить Apache после внесения изменений)
nano -w /etc/modsecurity/modsecurity.conf
# PCRE Tuning
# We want to avoid a potential RegEx DoS condition
#
SecPcreMatchLimit 500000
SecPcreMatchLimitRecursion 250000
Проблема 2
Message: Warning. Match of "within %{tx.allowed_request_content_type}" against "TX:content_type" required. [file "/etc/crs4/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "1012"] [id "920420"] [msg "Request content type is not allowed by policy"] [data "|multipart/form-data|"] [severity "CRITICAL"] [ver "OWASP_CRS/4.10.0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-protocol"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/255/153"] [tag "PCI/12.1"]
Объективно у нас отсутствует Content Type = multipart/form‑data. Фиксим
nano -w /etc/crs4/crs-setup.conf
SecAction \
"id:900220,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:'tx.allowed_request_content_type=|application/x-www-form-urlencoded| |multipart/form-data| |text/html|'"
Проблема 3 - фундаментальная
Message: Warning. Found 24 byte(s) in ARGS:name outside range: 32-36,38-126. [file "/etc/crs4/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "1611"] [id "920272"] [msg "Invalid character in request (outside of printable chars below ascii 127)"]
пример
ARGS:name: \xd0\x9e\xd1\x82\xd0\xbb\xd0\xb0\xd0\xb4\xd0\xba\xd0\xb0
Все дело в том, что OWASP CRS отказывается в кириллические кодировки, а у нас именно тот случай. (я пробовал подсунуть ему и windows-1251 charset, и koi8-r — безрезультатно. Разработчик отделывается общими фразами аля «дефолтный конфиг работает». Не знаю, не проверял, да и зачем нам дефолт? Что либо с этим сделать не понижая PL практически невозможно, посему пишем байпас.
SecRule ARGS:name "@rx (.*)" \
"id:001001001,\
phase:1,\
pass,\
nolog,\
msg:'Charset Check Bypass - Cyrilic encoding is not supported',\
ctl:ruleRemoveById=920272"
Проблема 4
Message: Warning. Matched phrase "var/log" at ARGS:html. [file "/etc/crs4/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf"] [line "116"] [id "930120"] [msg "OS File Access Attempt"] [data "Matched Data: var/log found within ARGS:html: ***** tail -f /var/log/apache2/modsec_audit.log
Здесь он уже умничает при анализе содержимого страницы :-) Выхода три: править сигнатуру, писать байпас либо не использовать ругательных слов в контенте. Первое отработает ровно до первого апдейта, третье, в нашем случае, не вариант. Пишем байпас.
SecRule ARGS:html "@rx (.*)" \
"id:001001002,\
phase:1,\
pass,\
nolog,\
msg:'Matched pattern bypass',\
ctl:ruleRemoveById=930120"
Проблема 5 - отсюда вытекающая
По факту мы с вами столкнулись с тем, что у нас огромное количество ложных срабатываний по причине используемой кодировки и по причине весьма специфичного контента. Что же делать? Глобально — либо понижать PL либо писать байпасы (выбор ваш). Плохо и то, и другое. Поэтому учитывайте специфику при подборе WAF.
Я уже был готов понижать PL и вовсю парсил логи, как пришло озарение :-) 99% процентов срабатываний происходит в момент публикации контента из админки. Что это значит? Не более чем то, что мы можем привязаться к HTTP REQUEST_METHOD и сделать глобальный байпас всего.
Плюсы‑минусы? Плюсы — избавимся от срабатываний по кодировке и истерии по поводу скриптового контента. Минусы — на сайте есть формы поиска и авторизации, так что в них можно будет долбиться в обход политик.
Выбор в целом сводится к тому чтобы либо принять риски, либо писать 100 500 правил без гарантии отсутствия ложных срабатываний в будущем. Я эти риски принял. В целом можно прикрутить IPS с сигнатурами на POST метод и спать несколько спокойнее.
Мои кривые потуги в парсер если кому будет нужно
echo > /var/log/apache2/modsec_audit.log
cat /var/log/apache2/modsec_audit.log | grep -iE ".*notice.*|*.warning.*|.*error.*|.*critical.*" | perl -ne 'm/.*(ARGS:\w+)/ && print "$1\n"' | sort | uniq >> /var/tmp/args.txt
cat /var/log/apache2/modsec_audit.log | grep -iE ".*notice.*|*.warning.*|.*error.*|.*critical.*" | perl -ne 'm/ARGS:html.*(id "\d+")/ && print "$1\n"' | sort | uniq >> /var/tmp/args_html.txt
cat /var/tmp/args_html.txt | sed 's/id \"/ctl:ruleRemoveById=/g' | sed 's/\"/\,\\/g' >> /var/tmp/html_rules.txt
Правило для админки (даем два, ибо автосохранение реализовано через PUT)
SecRule REQUEST_METHOD "@rx (POST|PUT)" \
"id:001001001,\
phase:1,\
pass,\
nolog,\
msg:'Admin Panel WAF bypass',\
ctl:ruleEngine=Off"
В принципе в энтерпрайзе можно привязаться к блоку доверенных адресов и выше описанные проблемы уйдут. Например так.
SecRule REMOTE_ADDR "@ipMatchFromFile admin_panel_whitelist.txt" \
"id:001001001,\
phase:1,\
pass,\
log,\
msg:'Admin Panel WAF bypass - POST|PUT only',\
ctl:ruleEngine=Off,\
chain"
SecRule REQUEST_METHOD "@rx (POST|PUT)"
Еще раз проверяем логи на предмет срабатываний и закручиваем порог блокировки
nano -w /etc/crs4/crs-setup.conf
SecAction \
"id:900110,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.inbound_anomaly_score_threshold=5,\
setvar:tx.outbound_anomaly_score_threshold=4"
Проверяем логи
Action: Intercepted (phase 2)
Apache-Handler: application/x-httpd-php
Stopwatch: 1737805433007092 8481 (- - -)
Stopwatch2: 1737805433007092 8481; combined=5102, p1=911, p2=2835, p3=0, p4=0, p5=1355, sr=75, sw=1, l=0, gc=0
Response-Body-Transformed: Dechunked
Producer: ModSecurity for Apache/2.9.7 (http://www.modsecurity.org/); OWASP_CRS/4.10.0.
Server: Apache
Engine-Mode: "ENABLED"
Если у вас стоит Fail2Ban, можно глянуть и туда
fail2ban-client status apache-modsecurity
Status for the jail: apache-modsecurity
|- Filter
| |- Currently failed: 0
| |- Total failed: 14
| `- File list: /var/log/apache2/error.log
`- Actions
|- Currently banned: 1
|- Total banned: 6
`- Banned IP list: 193.41.206.98
Бонусные правила
Первым делом закроем передачу сигнатур сервера
nano -w /etc/apache2/mods-enabled/security2.conf
# Manipulating server signaturte
ServerTokens Min
SecServerSignature "null"
systemctl restart apache2
Проверяем
curl -i --header 'Host: null.somedomain.name' https://null.somedomain.name | less
Server: null
Далее подумаем, а нужно ли нам такое :-) Представим, что мы хостим некий контент на который прямых ссылок нет и не предвидится. Технически — можно пройтись каким‑нибудь кроулером по сайту в поисках HTTP 200 и дальше уже с этим работать (вопрос сложности и целесообразности оставим в стороне)
Альтернативно представим что кто-то просто интересуется, а что же у нас доступно
Что с этим можно сделать? Описанная ситуация предполагает некое количество HTTP 404 возвращенных клиенту до того как он получит HTTP 200, а с этим уже можно работать.
-
ВАЖНЫЙ МОМЕНТ: (отдельное спасибо @Borelli) Если у вас большое количество изменений на сайте то высока вероятность того, что написав подобное правило валидные роботы поисковиков будут отлетать на равне с остальными
Что с этим делать если официальной информации о том, откуда приходят роботы поисковиков нет ? Казалось бы вариантов два:
Не использовать предложенный функционал при необходимости разрешить доверенных роботов
Парсить базы RIRов и добавлять ВСЕ адреса в белый список
Парсить базы конечно можно, но дело это такое себе, а функционал нам все таки нужен. Давайте разбираться в том, когда робот поисковика может получить от нас HTTP 404
В случае если ранее проиндексированная страница более недоступна
-
В случае если у нас расхождения в sitemap и широкие allow директивы в robots.txt
Предположим, что c sitemap и robots.txt у нас все в порядке. Что делать дальше ? А дальше мы сделаем исключение для всех опубликованных URI (включая те, что были проиндексированы и более недоступны) используя переменную REQUEST_URI
В нашем случае весь контент доступен по двум путям /shelves/* и /books/*
Пишем нечто подобное
#
# Block content harvesting
#
# SRC IP Whitelist
SecRule TX:REAL_IP|REMOTE_ADDR "@ipMatchFromFile http_404_whitelist.txt" \
"id:001003001,\
phase:1,\
pass,\
nolog,\
msg:'IP Whitelisted for HTTP 404',\
ctl:ruleRemoveById=001003002,\
ctl:ruleRemoveById=001003003,\
ctl:ruleRemoveById=001003004"
# Bypass for published resources
SecRule REQUEST_URI "@rx (\/shelves(|\/).*|\/books(|\/).*)" \
"id:001003002,\
phase:1,\
pass,\
nolog,\
msg:'URI Whitelisted for HTTP 404',\
ctl:ruleRemoveById=001003003,\
ctl:ruleRemoveById=001003004"
SecRule RESPONSE_STATUS "@streq 404" \
"id:001003003,\
phase:3,\
pass,\
setvar:IP.bad_http_request=+1,\
expirevar:IP.bad_http_request=86400,\
log,\
msg:'Page Not Found - HTTP 404 - Count %{IP.bad_http_request}'"
SecRule IP:BAD_HTTP_REQUEST "@gt 10" \
"id:001003004,\
phase:3,\
drop,\
log,\
msg:'Client Exceeded HTTP 404 Request Limit of 10 - Banned For a Day'"
Здесь правило 001003001 - это байпас функции для доверенных адресов
Правило 001003002 - байпас для опубликованных URI (по ним у нас будут ходить как боты поисковиков, так и все остальные)
Правило 001003003 - это проверка ответа на наличие HTTP 404 и выставление переменной IP.bad_http_request
Далее правило 001003004 дропает сессию при условии значения IP.bad_http_request более 10
Project Honey Pot
Что это за зверь такой? Не более чем база IP адресов ботов (впрочем, чуть более чем просто база, но об этом далее).
Установка "ловушки"
Несколько слов о том, зачем это все — в первую очередь позволяет собрать дополнительную статистику о разных ботах: роботах, кроулерах, спамерах (в том числе в камментах) и прочих харвестерах.
Работает все до нельзя просто:
Ханипот генерит скрипт, который потом можно захостить, активировать и тем самым получить свой ханипот
Дальше задача насытить сайт ссылками
Ну а дальше ждем пришествия роботов «прокликивающих» абсолютно все, в том числе нашу «ловушку»
Дальше данные о попаданце сливаются на ханипот и, собственно, к нам
Так же скрипт позволяет позволяет скормить ботам некоторое количество почтовых адресов и если на эти адреса пойдет спам рассылка — отправитель попадает в черный список
Надеюсь вы поняли
Приступим!
Регистрируемся на сайте и скачиваем скрипт для интеграции с сайтом (в нашем случае похопе).
Публикуем и выставляем разрешения
chown -R root:www-data /var/www/bookstack/public/hello.php
chmod 644 /var/www/bookstack/public/hello.php
Вызываем браузером и активируем
Добавляем в footer ну или куда‑либо еще (только так, чтоп человекам не видно было)
<footer class="print-hidden">
<a href="https://null.somedomain.name/hello.php" target="_blank" rel="noopener"></a>
</footer>
Альтернативный способ — инджектить напрямую из ModSecurity. Вот так
SecContentInjection On
SecStreamOutBodyInspection On
SecRule RESPONSE_CONTENT_TYPE "@contains text/html" \
"id:001003001,\
phase:4,\
pass,\
nolog,\
chain"
SecRule STREAM_OUTPUT_BODY "@rsub s/<\/html>/<a href=\"https:\/\/null.somedomain.name\/hello.php\"><\/a><\/html>/"
Привязываем черные списки к ModSecurity
Как было понятно из предыдущей главы, если где‑то есть база не очень хороших дядь и теть, то было бы странно ей не воспользоваться.
Генерируем ключ доступа к API на сайте проекта
Создаем правило (убедитесь что его айдишник отрабатывает первым)
nano -w /etc/crs4/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
# HTTPBL Check
SecHttpBlKey API ключ
SecRule TX:REAL_IP|REMOTE_ADDR "@rbl dnsbl.httpbl.org" \
"id:001001001,\
phase:1,\
capture,\
block,\
log,\
msg:'HTTPBL Match of Client IP.',\
logdata:'%{tx.httpbl_msg}',\
setvar:tx.httpbl_msg=%{tx.0},\
chain"
SecRule TX:0 "threat score (\d+)" "chain,capture"
SecRule TX:1 "@gt 20"
Заключение
На этом пока остановимся. Впереди Snort, DDoS Offloading, возможно SIEM (логи смотреть было бы крайне полезно).
Спасибо всем кто дочитал до конца! По традиции — кросс линк ProjectNull (сеошник во мне не умер :-)
Буду рад ответить на вопросы по теме.
Комментарии (20)
Sadok
25.01.2025 21:16букв много. по опыту скажу, что использование WAF - палка с N концами. "ой! о что это у нас платежи не ходят?!! - а это у нас правила WAF обновились, пардон." так что мы эту штуку у себя на балансерах отключали. заказчик хочет - пусть сам у себя на бэкендвх играется.
igorv126 Автор
25.01.2025 21:16На этот случай, если мы говорим о критических приложениях, у вас наверняка есть пре-прод. Собственно все обновления и тесты - в первую очередь там
erley
25.01.2025 21:16У меня почему-то когнитивный диссонанс - решение на mod_security нельзя назвать удобным и мощным (на мой взгляд).
То есть там где дело доходит до того что люди себе ставят задачу развернуть-таки WAF (то есть у них и правда что-то критичное есть что защищать нужно) - там уж как-то не мелочатся, а разворачивают решение с наглядной визуализацией что происходит, кучей метрик и эвристик. У вас не будет времени залезать в текстовые конфиги и править правила когда что-то действительно будет атаковано. А "выдернуть сетевой кабель" не всегда возможно физически и/или функционально.
Решение на апаче с этим модулем требует ещё много всего чтобы им можно было пользоваться на таких критичных проектах.
Однако тема интересная, очень любопытно будет почитать про snort и другие open source решения, буду ждать!
PS У нас используется Imperva и кое-где Palo Alto (у этих правда какие-то косяки в последнее время).
igorv126 Автор
25.01.2025 21:16Почему критичных ? Я сразу оговорился, что для критичных оно того не стоит
jhoag
25.01.2025 21:16Тема интересная в рамках самообразования. Но как категория программного обеспечения WAF всегда казался мне костыльным костылём — платой за низкое качество веб-приложений и системного администрирования. То есть вместо развития культуры безопасной разработки и сисопс индустрия породила ещё одну сущность, которая прикрывает как можно больше ленивых задниц — WAF. Единственное, что кажется в таких фаерволах правда актуальным, — защища от ботов, но и для этого есть более нишевые решения.
igorv126 Автор
25.01.2025 21:16Да, наверное вы правы.
Впрочем если лезть совсем уж в глубины, то сегментация критической инфраструктуры предполагает использования физически разных девайсов (местами от разных вендоров) с проверками, выполняемыми в каждой из зон (в том числе на уровне приложения и операционной системы)
Borelli
25.01.2025 21:16Из примеров не понятно, что будет с валидными парсерами валидных поисковиков? Для них отдельный
котёлнабор правил существует или надо добавлять все подсети яндексов-гуглов в белый список?igorv126 Автор
25.01.2025 21:16Да, они будут отлетать на равне с остальными.
Все-же конкретно эта задача изначально ставилась под ресурс не предполагающий обхода доверенными роботами. И да, все как вы и говорите, если официальной информации о том, откуда приходят роботы поисковиков нет - выхода два:
- Не использовать предложенный функционал при необходимости разрешить доверенных роботов- Парсить базы RIRов и добавлять ВСЕ адреса в белый список
Если мне удастся отловить трафик Яндекса или Гугло бота, может быть в нем найдется некий идентификатор к которому можно будет привязаться
igorv126 Автор
25.01.2025 21:16Придумал ! Все же парсить риров - дело такое себе. Есть способ проще. Отпишу чуть позднее
igorv126 Автор
25.01.2025 21:16Готово ! Сделал REQUEST_URI исключение через chain
Статью обновил. Вам - спасибо ! :)
igorv126 Автор
25.01.2025 21:16Здесь был коммент от кого-то с просьбой написать про modsecurity3 под nginx (толстые пальцы нажали не в ту кнопку и комментарий пропал, сорян)
Возможно когда-то и соберусь в nignx, но знаний по нему у меня ноль, ровно как и практических задач
x89377
Полезная статья. Спасибо.
Хотя мне пришлось самому написать для IIS на .NET аналог ModSecurity, но в целом всё также. Правила тоже брал OWASP CRS.