Обеспечение безопасности веб-сайта на базе ОС Linux (в примере будет использован Linux Debian), nginx, naxsi, iptables и fail2ban:

  • nginx — веб-сервер;
  • naxsi — web application firewall;
  • также нам потребуется fail2ban и iptables.

Установка и первичная настройка nginx и naxsi

Компилируем и устанавливаем nginx с модулем naxsi. Я использую следующий скрипт для автоматической компиляции:

# mkdir -p /opt/src/
# nano /opt/src/nginx_compiler.sh

#!/bin/bash

## Read nginx src URL
nginx_url="$1";
 
## Check correct url
if [[ "$nginx_url" =~ ^http\://nginx.org/download ]]; then
 echo "URL ok";
else
 echo "Wrong Nginx src URL. Exit"
 exit;
fi

## Env
work_dir="/opt/src";
nginx_dir="/etc/nginx";
naxsi_dir="$work_dir/naxsi-master";
backup_dir="/tmp/nginx.bak.`date +%F`";

## Nginx src
cd "$work_dir";
# Extract Nginx src
wget "$nginx_url" -O- | tar xvzpf - -C "$work_dir"
nginx_src="$work_dir/`ls $work_dir | grep "nginx-" | grep -v 'gz' | tail -1`";
echo "Nginx dir: $nginx_src";

## naxsi-latest
## Remove old version
rm -rf $naxsi_dir;

## Download latest version
git clone --recursive https://github.com/nbs-system/naxsi $naxsi_dir;

## Create backup
echo "Create backup for nginx"
cp -r $nginx_dir $backup_dir;

## Compile nginx
service nginx stop
sleep 3;
echo "Compile nginx"
cd $nginx_src
echo "$nginx_src";
./configure \
--prefix=$nginx_dir \
--conf-path=$nginx_dir/nginx.conf \
--sbin-path=/usr/sbin \
--http-log-path=/var/log/nginx/access.log \
--error-log-path=/var/log/nginx/error.log \
--pid-path=/var/run/nginx.pid \
--user=www-data \
--group=www-data \
--with-http_ssl_module \
--with-http_realip_module \
--add-module=$naxsi_dir/naxsi_src \
&& make && checkinstall && cd $work_dir

if [ "$?" -eq "0" ]; then
 ## Delete src
 rm -rf $nginx_src;
 rm -rf $naxsi_dir;

 ## Restore nginx configuration
 echo "Restoring configurations";
 cp -r $backup_dir/* $nginx_dir/;
 service nginx start;
fi
exit 0;

Установим необходимые для сборки пакеты:

# apt-get install git make checkinstall libssl-dev

И запустим скрипт. В его параметр достаточно подставить ссылку на последнюю версию nginx, модуль naxsi подгрузится автоматически.

# chmod +x /opt/src/nginx_compiler.sh
# /opt/src/nginx_compiler.sh http://nginx.org/download/nginx-1.8.0.tar.gz

После компиляции nginx с модулем naxsi необходимо произвести настройку конфигурационного файла nginx и виртуального хоста. Убедимся, что в настройках nginx имеется строка с каталогом размещения виртуальных хостов и правилами WAF, в противном случае добавим эти строки:

# grep 'sites-enabled' /etc/nginx/nginx.conf

include /etc/nginx/sites-enabled/*;

# grep '/etc/nginx/naxsi_core.rules' /etc/nginx/nginx.conf

include /etc/nginx/naxsi_core.rules;

Файл naxsi_core.rules содержит паттерны, по которым и происходит анализ обращений к серверу на предмет атаки. Далее переходим к настройке vhost:

# nano /etc/nginx/sites-enabled/example.com

server {
 listen 80;
 server_name example.com;
 access_log /var/log/nginx/example.com_access.log;
 error_log /var/log/nginx/example.com_error.log;
 ## Отключаем WAF для IP администратора
 if ($remote_addr = "MY_EXTERNAL_IP") {
  set $naxsi_flag_enable 0;
 }

 location / {
  root /var/www;
  include /etc/nginx/naxsi.rules;
 }
}

После перезапуска nginx модуль naxsi будет фиксировать атаки в /var/log/nginx/example.com_error.log. Формат записей атак будет следующего вида:

2015/04/24 14:47:31 [error] 5560#0: *128787 NAXSI_FMT: ip=ххх.ххх.ххх.ххх&server=example.com&uri=/&learning=0&vers=0.54&total_processed=3426&total_blocked=67&block=1&cscore0=$EVADE&score0=4&zone0=HEADERS&id0=1402&var_name0=content-type, client: ххх.ххх.ххх.ххх, server: example.com, request: "GET / HTTP/1.1", host: "example.com"

Атаки фиксируются, но не будут блокироваться до тех пор, пока не будет деактивирован «режим обучения» в файле /etc/nginx/naxsi.rules:

LearningMode;

Смысл режима обучения заключается в том, что в отличие от обычного («боевого») режима, атака фиксируется в логе, но блокировака не происходит, в то время как в боевом режиме в случае атаки клиенту отдается страница «/RequestDenied» (при настроках по-умолчанию, секция DeniedUrl файла naxsi.rules). Для деактивации необходимо закоментировать строку «LearningMode;» и перезапустить nginx, но к этому мы вернемся чуть позже. На этом процесс сброки и первичной настройки WAF закончен.

Настройка iptables и fail2ban

iptables в качестве первичной защиты от Dos-атак

Для первичной защиты от Dos-атак ограничим количество одновременных подключений к веб-серверу с одного IP-адреса:

# apt-get install iptables
# iptables -A INPUT -i eth0 -p tcp --syn --dport 80 -m state --state NEW -m recent --update --seconds 1 --hitcount 20 -j REJECT --reject-with tcp-reset
# iptables -A INPUT -i eth0 -p tcp --syn --dport 443 -m state --state NEW -m recent --update --seconds 1 --hitcount 20 -j REJECT --reject-with tcp-reset
# iptables -A INPUT -i eth0 -p tcp -m multiport --dports 80,443 -j ACCEPT

Правила не позволят устанавливать больше 20 соединений в секунду. При желании можно сократить количество соединений, увеличив количество секунд или уменьшив значение hitcount. Для hitcount значение 20 является максимальным.

fail2ban

Сервис fail2ban предназначен для блокирования доступа. Читая лог-файлы других сервисов, fail2ban сверяет их содержимое с правилами, и если события в логах удовлетворяют условию, выполняет заданное администратором действие. В нашем случае мы будем использовать сервис для блокирования доступа с IP адресов, пытающихся атаковать наш веб-сервер. Произведем установку и настройку сервиса:

# apt-get install fail2ban
# nano /etc/fail2ban/jail.conf

Добавим в конфиг секцию nginx-naxsi

[nginx-naxsi]

enabled  = true # Правило активно
port     = http,https # Порты, над которыми будет выполняться действие по блокировке
filter   = nginx-naxsi # Название фильтра
logpath  = /var/log/nginx/*error.log # Путь к логам для анализа. Маска * использована для анализа нескольких лог-файлов
maxretry = 10 # Количество совпадений, при которых срабатывает фильтр
findtime = 20 # Период времени, при котором учитывается количество совпадений
bantime  = 600 # Время блокировки

Далее произведем настройку фильтра nginx-naxsi:

# nano /etc/fail2ban/filter.d/nginx-naxsi.conf

[Definition]
failregex = .*NAXSI_FMT.*ip=<HOST>&server=.*

Готово. Перезапускаем fail2ban.

# service fail2ban restart

Таким образом, fail2ban анализируя error-log nginx, будет искать соотвествие строки:

2015/04/24 14:47:31 [error] 5560#0: *128787 NAXSI_FMT: ip=123.123.123.123&server=example.com&uri=

и в случае обнаружения извлекать IP-адрес. Как только в интервале в 20 секунд количество таких строк составит 10 (для каждого IP-адреса), fail2ban добавит в цепочку iptables на 600 секунд (опция bantime) правило, блокирующее подключение к серверу для данного IP адреса. По умолчанию правило имеет следующий вид:

iptables -I fail2ban- 1 -s -j DROP

Информация по забаненным и разбаненным хостам доступна в файле /var/log/fail2ban.log и имеет следующий вид:

2015-05-29 11:23:51,343 fail2ban.actions: WARNING [nginx-naxsi] Ban 123.123.123.123
2015-05-29 11:33:51,973 fail2ban.actions: WARNING [nginx-naxsi] Unban 123.123.123.123
2015-05-29 12:20:57,913 fail2ban.actions: WARNING [nginx-naxsi] Ban 123.123.123.123
2015-05-29 12:30:58,543 fail2ban.actions: WARNING [nginx-naxsi] Unban 123.123.123.123

Правила блокировки расположены в каталоге /etc/fail2ban/action.d. Система обеспечения безопасности веб-сайта готова, рассмотрим вариант перевода WAF  в «боевой» режим работы.

Настройка white-листов naxsi и отключение режима обучения

В white-листах содержатся исключения для станадртного набора правил (файл naxsi_core.rules). Чтобы понять логику создания исключений, рассмотрим лог атаки:

2015/04/24 14:47:31 [error] 5560#0: *128787 NAXSI_FMT: ip=ххх.ххх.ххх.ххх&server=example.com&uri=/&learning=0&vers=0.54&total_processed=3426&total_blocked=67&block=1&cscore0=$EVADE&score0=4&zone0=HEADERS&id0=1402&var_name0=content-type, client: ххх.ххх.ххх.ххх, server: example.com, request: "GET / HTTP/1.1", host: "example.com"

Запись разделена на секции:

  • ip — IP-адрес клиента;
  • server — доменное имя сервера;
  • uri — идентификатор ресурса;
  • learning — сообщеает, активирован ли режим обучения (0) илинет (1);
  • vers — версия naxsi;
  • total_processed — количество обработанных запросов воркером nginx;
  • total_blocked — количество заблокированных naxsi запросов воркера;
  • block — сообщает, заблокирован запрос (1) или нет (0);
  • cscore0 — тег группы сигнатур, под который попадает атака;
  • score0 — уровень атаки;
  • zone0 — зона, в которой обнаружена атака;
  • id0 — идентификатор сигнатуры, под которое попадает атака;
  • var_name — имя переменной, в которой обнаружена атака.

С первыми семью секциями все понятно, перейдем к тем, что нас интересуют.

CscoreX информирует нас о теге, под который попадает атака. В нашем случае тег $EVADE сообщает о том, что была попытка выполнить обход чего-либо. К примеру, закодировать в UTF-8 вредоносный код. А «X» — сообщает нам о том, что в данном запросе это первая по счету блокировка.

Score0 информирует нас об уровне атаки.  В данном случае она равна 4. Кстати, в файле naxsi.rules, имеющий следующий вид:

LearningMode;
SecRulesEnabled;
DeniedUrl "/RequestDenied";

## check rules
CheckRule "$SQL >= 8" BLOCK;
CheckRule "$RFI >= 8" BLOCK;
CheckRule "$TRAVERSAL >= 4" BLOCK;
CheckRule "$EVADE >= 4" BLOCK;
CheckRule "$XSS >= 8" BLOCK;

указано, при каком уровне должен отрабатывать naxsi.

Zone0 сообщает нам зону, в которой произошло совпадение с сигнатурой. Список зон представлен ниже:

  • ARGS — GET args;
  • HEADERS — HTTP Headers;
  • BODY — POST args;
  • URL — The URL (before ‘?’);
  • FILE_EXT — Filename (in a multipart POST containing a file).

id0 информирует нас об идентификаторе сигнатуры, с которой произошло совпадение. В данном случае 1402 указывает нам на то, что была попытка выполнить попытку передать на сервер данные или файл. Если такой функционал на сайте не реализован, то, соотвественно, такой запрос может позакаться подозрительным.

var_name0 сообщает нам о названии поля заголовка, содержащее подозрительный контент.

Мы без труда можем найти сигнатуру, по которой произошла блокировка. В файле naxsi_core.rules найдем строку, содержащую «id:1402». Вот это правило:

MainRule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402;

Создадим правило-исключение для данной сигнатуры. К примеру, наш сайт использует форму авторизации и данные передаются через форму, используя заголовок «Content-Type: application/x-www-form-urlencoded». Для создания white-листа необходимо добавить файл исключения в конфиг vhost:

server {
 listen 80;
 server_name example.com;
 access_log /var/log/nginx/example.com_access.log;
 error_log /var/log/nginx/example.com_error.log;
 
 ## Отключаем WAF для IP администратора
 if ($remote_addr = "MY_EXTERNAL_IP") {
  set $naxsi_flag_enable 0;
 }

 location / {
  root /var/www;
  include /etc/nginx/naxsi.rules;
  <strong>include /etc/nginx/naxsi_rule.wl;</strong>
 }
}

и создать соотвествующий файл:

# nano /etc/nginx/naxsi_rule.wl

следующего содержания:

BasicRule wl:1402

После перезапуска nginx сигнатура 1402 будет игнорироваться naxsi для текущего виртуального хоста.

Игнорирование сигнатуры #1000 для всех ссылок для GET аргументов ‘foo’

BasicRule wl:1000 "mz:$ARGS_VAR:foo";

Игнорирование сигнатуры #1000 для GET аргументов ‘foo’ для ссылки ‘/bar’

BasicRule wl:1000 "mz:$ARGS_VAR:foo|$URL:/bar";

Игнорирование сигнатуры #1000 для всех GET аргументов для ссылки ‘/bar’

BasicRule wl:1000 "mz:$URL:/bar|ARGS";

и т.д. Но на практике white-листу naxsi не хватает гибкости — поиска по шаблону. Он отсуствует в готовых сборках nginx-naxsi для Debian (используется устаревшая версия naxsi), но в нашем случае все гораздо лучше. Начиная с версии 0.52 naxsi поддерживает регекспы. Рассмотрим пару примеров:
Игнорирование сигнатуры #1000 во всех GET аргументах, содержащих ‘bla’

BasicRule wl:1000 "mz:$ARGS_VAR_X:bla";

Игнорирование сигнатуры #1000 в GET аргументах, имеющих название ‘bla’ :

BasicRule wl:1000 "mz:$ARGS_VAR_X:^bla";

Игнорирование сигнатуры #1000 во всех GET аргументах, содержащих bla_<цифры> :

BasicRule wl:1000 "mz:$ARGS_VAR_X:^bla_[0-9]+$"

P.S.

C подробной документацией naxsi на английском языке можно ознакомиться в wiki.

Парсинг и визуализацию логов naxsi, а также создание white-листов можно осуществлять с помощью утилиты nxapi.