Пару недель назад команда Vulners опубликовала сравнение нескольких популярных WAF. Поймав себя на мысли - "а как оценивать качество его работы?", я решил разобрать подробнее тему security-тестов и критериев оценки Web Application Firewall. Статья пригодится, в первую очередь, тем, кому интересна тема веб-безопасности, ровно как и счастливым обладателям WAF.

Критерии оценки

Сравнивая различные решения, мы привыкли ориентироваться на показатели скорости и стабильности работы, удобства настройки, управления, обновления и масштабирования. В контексте WAF к этому списку нужно добавить и точность выявления атак - количество пропусков (False Negative) и ложных срабатываний (False Positive).

Зоопарк из UTF-8

Чтобы понять, сколько тонкостей существует при анализе запросов, рассмотрим в качестве примера особенность работы с UTF-8. Если в поисковой строке сайта мы наберем что-то в английской раскладке, то, скорее всего, будет использоваться набор Basic Latin.

UTF-8: Basic Latin
UTF-8: Basic Latin

При этом запрос, который будет передаваться в веб-приложение, выглядит следующим образом: GET /?s=test

Ищем "test", фиксируем вывод
Ищем "test", фиксируем вывод

Теперь попробуем использовать набор символов из Halfwidth and Fullwidth Forms (ищем тот же test, только с другим набором символов).

UTF-8: Halfwidth and Fullwidth Forms
UTF-8: Halfwidth and Fullwidth Forms

Выглядит так, будто мы использовали пробел между символами, но на самом деле - нет.

Используем другой набор символов и получаем тот же результат в поисковой выдаче
Используем другой набор символов и получаем тот же результат в поисковой выдаче

Запрос (в URLencode): GET /?s=%EF%BD%94%EF%BD%85%EF%BD%93%EF%BD%94

При этом оба запроса (с разными наборами символов) приведут к выводу одного и того же результата - объекта, содержащего вхождение "test".

Меняем "test" на "tes1", чтобы убедиться в корректности работы поиска - ничего не выводится
Меняем "test" на "tes1", чтобы убедиться в корректности работы поиска - ничего не выводится

Сперва мы подумали, что само веб-приложение (проверяли на WordPress, но отработает и на других) производит нормализацию данных до Basic Latin, но в итоге выяснили, что данные передаются без нормализации напрямую в БД. Есть ограничения, связанные с использованием Halfwidth and Fullwidth Forms - его нельзя применять при написании оператора SELECT, но можно использовать, к примеру, в конструкции LIKE '%Текст в Halfwidth and Fullwidth Forms%'. Этот набор символов может использоваться не только при составлении SQL-запросов. Есть примеры его применения при поиске XSS и т.д.

Данная особенность позволяет выполнять обход WAF, если он не нормализует символы до Basic Latin. Существует множество техник обхода, и было бы здорово создать инструмент, позволяющий разом их все проверить.

WAF Bypass: инструментарий

На github можно найти множество решений, позволяющих тестировать WAF, есть даже специальные аккаунты, производящие сбор такого инструментария, например waflib.

Помимо основного Unit-теста, который используем для тестирования Nemesida WAF перед каждым релизом (тест на каждую версию ОС занимает примерно 30 часов), мы разработали собственный waf-bypass, позволяющий оценить точность выявления атак. Но всегда интересно и полезно проверить работу продукта с использованием сторонних решений, поэтому в качестве второго (а в рамках этой статьи - основного) инструмента я выбрал Go Test WAF, размещенный в git-репозитории пользователя wallarm.

Для использования waf-bypass, написанного на Python3, необходимо клонировать проект, установить зависимости и запустить скрипт:

git clone https://github.com/nemesida-waf/waf_bypass.git /opt/waf-bypass/
python3 -m pip install -r /opt/waf-bypass/requirements.txt
python3 /opt/waf-bypass/main.py --host='example.com'

Если следовать документации, Go Test WAF можно запускать из докер-контейнера, что я и сделал:

docker build . --force-rm -t gotestwaf
docker run gotestwaf --url='http://example.com'

Если проводить сравнение инструментов исходя из их использования, то Go Test WAF работает быстрее (за счет многопоточности) и содержит небольшой набор для проверки False Positive. Учтем это в следующих релизах waf-bypass.

Вижу цель, иду к ней!

Тестировать буду 2 версии: Nemesida WAF (полноценную с машинным обучением) и Nemesida WAF Free (бесплатную, но только с сигнатурным анализом).

Когда увидел результаты тестов WAF
Когда увидел результаты тестов WAF

В обзоре Vulners в финальной таблице максимальным результатом WAF, полученным путем тестирования Go Test WAF, являлось значение 83.06%, при этом ModSecurity набрал 49.82%. Интересно посмотреть, сколько сможет набрать Nemesida WAF и Nemesida WAF Free.

Nemesida WAF Free bypass

Напомню, анализ в Nemesida WAF Free производится только сигнатурным методом, при этом эта версия имеет качественно написанную и автоматически обновляемую базу сигнатур (ознакомиться с которой можно по ссылке), а также Личный кабинет (также устанавливается локально) - веб-интерфейс для визуализации событий. Nemesida WAF Free представлена в виде динамического модуля, который можно подключать как к новому экземпляру Nginx, так и к уже установленному (или скомпилированному с собственными флагами). Обновляется Nemesida WAF Free из репозитория и доступен для Debian, Ubuntu и CentOS, "быстрый старт" займет 5-7 минут для опытных пользователей.

Часть содержимого первой страницы Личного кабинета Nemesida WAF Free
Часть содержимого первой страницы Личного кабинета Nemesida WAF Free

Запускаем Go Test WAF, смотрим результаты.

Nemesida WAF Free: GoTestWAF
Nemesida WAF Free: GoTestWAF

45.47% и 0 ложных срабатываний - довольно неплохо, учитывая, что часть тестов содержит пейлоады в Base64, которые не декодируются в Nemesida WAF Free. Теперь ловким движением руки и не меняя настроек Nemesida WAF Free, проверим его с помощью waf-bypass:

Nemesida WAF Free: waf-bypass
Nemesida WAF Free: waf-bypass

325 пропусков и 1054 заблокированных пейлоадов. Пытаемся запомнить результаты и двигаемся дальше.

Nemesida WAF bypass

Помимо функционала нормализации данных, Nemesida WAF позволяет производить декодирование запросов в различных кодировках, в том числе и в Base64, поэтому было особенно интересно, какой скор он сможет набрать. Начнем с waf-bypass, запускаем, смотрим результаты:

Nemesida WAF: waf-bypass
Nemesida WAF: waf-bypass

4 пропуска, остальные запросы заблокированы. Здорово, но все-таки waf-bypass - наша разработка, поэтому очень интересно, какие результаты будут получены при тестировании с использованием Go Test WAF. Запускаем Go Test WAF, смотрим результаты:

Nemesida WAF: gotestwaf
Nemesida WAF: gotestwaf

Проверка заняла немного больше времени, но и показатели в 2 раза лучше - 94.45%, и на 11.39% выше, чем наилучший результат в обзоре Vulners. Почти отсутствуют пропуски, но из 8 False Positive заблокировано 5, а хотелось, чтобы 0, поэтому будем разбираться. Сперва посмотрим содержимое всех 8 FP-запросов:

- union was a great select
- 'h2<h1'
- D'or 1st parfume
- "1) a-b=c"
- john+or@var.es
- DEAR FINN,--I think it would do; copy should reach us second post
- The Senora found herself a heroine; more than that, she became aware time he came.

Из них ошибочно заблокированные:

- f971e9ace6=union was a great select
- 24d85a0990=D'or 1st parfume
- 48105a7f77=john+or@var.es
- 18196a6191=DEAR FINN,--I think it would do; copy should reach us second post
- e7e5421304=The Senora found herself a heroine; more than that, she became aware

Чтобы понять, почему запросы были заблокированы, нужно понимать, как работает модуль машинного обучения в Nemesida WAF - построение моделей происходит за счет использования 2-х наборов обучающих выборок - базы атак и базы условно чистого трафика. Чем меньше реальных запросов попало в обучающую выборку - тем больше будет False Positive. В данном случае в обучающей выборке не попадались такие или схожие запросы, кроме этого, они содержат признаки атаки (например, --, или union ... select, или 'or), поэтому были заблокированы. Используя модуль дообучения Nemesida WAF Signtest, производим экспорт False Positive в один клик, после чего они войдут в обучающую выборку и не будут учитываться - как сами, так и другие, схожие с ними запросы.

Запускаем тест заново:

Nemesida WAF: gotestwaf (с FP в обучающей выборке)
Nemesida WAF: gotestwaf (с FP в обучающей выборке)

Несмотря на то, что содержимое запросов имеет динамический контент 24d85a0990 в 24d85a0990=D'or 1st parfume, ложные срабатывания отсутствуют, при этом количество пропусков не изменилось. Скоринг стал меньше, а должен был увеличиться - вероятно, ошибка в самом Go Test WAF.

Чтобы разобраться в пропусках, я настроил журналирование Nginx таким образом, чтобы фиксировать содержимое запросов, не получивших код ответа 403:

Расширенное журналирование Nginx

Если вы хотите проверить, какие запросы не были заблокированы - активируйте расширенное журналирование в Nginx:

1. В секцию http {} добавив:

logformat extended '$msec $requesttime $timeiso8601 $timelocal $requestid $status $remoteaddr $scheme $host $request $httphost $httpcookie $httpuseragent $contentlength $contenttype $httporigin $requestbody\n';

и

map $status $loggable { 403 0; default 1; }

2. В секцию server {} файла виртуального хоста (например, /etc/nginx/conf.d/1.conf) добавив:

access_log /var/log/nginx/extended_access.log extended if=$loggable;

После перезапуска Nginx в /var/log/nginx/extended_access.log будут попадать все незаблокированные запросы (не получившие код ответа 403).

Фактически пропусков было всего 3, и об их критичности судите сами:

GET /AEAEAFAEAEAFetcAFpasswd

GET / QUIT /

GET /foo_bar=foo bar=foo[barfoo[bar

Визуализация атак: графики, поиск, отчеты

Вне зависимости от того, используется ли Nemesida WAF или Nemesida WAF Free, к любому из них можно подключить Личный кабинет - веб-интерфейс, позволяющий визуализировать работу модулей:

Личный кабинет: графики и диаграммы
Личный кабинет: графики и диаграммы
Личный кабинет: графики и диаграммы
Личный кабинет: графики и диаграммы
Личный кабинет: графики и диаграммы
Личный кабинет: графики и диаграммы
Nemesida WAF: информация об атаках
Nemesida WAF: информация об атаках

В Личном кабинете есть и другие разделы - информация по атакам методом перебора и SMS-флуде, статистика работы сканера уязвимостей и т.д. Также можно выгрузить общий отчет в формате PDF или CSV. Личный кабинет написан на Django и устанавливается локально, так же, как и другие модули.

Демонстрационный стенд: 

demo.lk.nemesida-security.com (demo@pentestit.ru / pentestit)

И пока на версусе решают, кто из них круче...

Как я писал выше, есть несколько показателей оценки качества работы ПО, для WAF одним из основных критериев является количество ложных срабатываний и пропусков, и было очень увлекательно и полезно выполнять проверки с использованием сторонних байпасеров, за что отдельная благодарность разработчикам Go Test WAF.

Кроме этого, такие тесты позволяют наглядно понять разницу между сигнатурным анализом и машинным обучением, необходимость использовать различные механизмы нормализации и декодирования. Но если по какой-то причине использовать коммерческий продукт нет возможности, всегда можно воспользоваться ModSecurity, NAXSI или Nemesida WAF Free с красивым и удобным Личным кабинетом. Nemesida WAF Free доступен в виде установочных дистрибутивов для Debian/Ubuntu/CentOS, в виде Virtual Appliance для KVM/VirtualBox/VMWare и образа для Docker-контейнера. Больше информации можно найти здесь.

Спасибо, что дочитали, если появятся какие-то мысли - можно обсудить в комментариях.