Введение в mod_security

Использование веб-приложений в Интернет иногда похоже на игру в
русскую рулетку. Не смотря на то, что достижение требуемого уровня
безопасности в Сети теоретически возможно, как всегда это расходится с
реальной жизнью. Необходима всего лишь единственная ошибка в коде,
чтобы злоумышленник получил неограниченный доступ к вашим данным. Если
вы используете веб-приложение со сложной функциональностью, то имеются
большие шансы, что оно имеет проблемы в своей безопасности. Например,
возьмем этот URL:

http://www.webapp.com/login.php?username=admin';DROP%20TABLE%20users--

Если ваше приложение уязвимо для SQL инъекций, то данный запрос может
удалить всю информацию о пользователях в вашем приложении. А вы
делаете ли регулярные «бэкапы» своей базы?

К счастью, модуль mod_security может защитить нас от множества
веб-атак, включая SQL инъекции.


Почему мне следует использовать mod_security?
Полтора года назад, перед тем, как я начал работать над mod_security,
для контроля своего веб-трафика, я использовал Short. Все прекрасно
работало. Я задал в Short слова, которые меня интересовали, и он
уведомлял меня всякий раз, когда одно из них появлялось во входящем
потоке. Но я хотел большего. Я хотел свободы в определении сложных
правил, а также возможности выполнения различных действий,
определенных в HTTP. Использование же IDS отнимает очень много
времени, а сама система является слишком дорогостоящей.

В то время я также пытался комбинировать использование mod_rewrite и
mod_setenvif. Используя mod_rewrite, очень легко обнаружить слова drop
и table, а затем перенаправить клиента с такого URL, тем самым
предотвратить атаку. Однако это, конечно, убережет вас от атак
начинающих хакеров, но более опытный злоумышленник может просто
использовать тот же URL, как и приведенный выше, но вместо метода GET
использовать метод POST. Так как переменные метода POST обычно не
обрабатываются в большинстве модулей, то данная атака успешно пройдет.

Убедившись в необходимости создания нового инструмента, я встал перед
выбором: взять Java и создать полноценный реверс-прокси или создать
модуль Apache, используя огромный объем существующего кода. Первый
вариант требовал большего времени на работу, а результат, вероятно,
был бы полезен для очень ограниченного количества людей. Я же хотел
создать что-то более гибкое и легкое в использовании, поэтому я выбрал
второй вариант.

Возвращаясь к нашему примеру URL, для предотвращения SQL инъекции
«drop table» с помощью mod_security, необходимо добавить в
конфигурацию Apache следующее:

SecFilter «drop[[:space:]]table»

Единственным параметром является регулярное выражения, на соответствие
которому будут проверяться все входящие запросы. Также это можно
достигнуть, используя mod_rewrite, но отличие в том, что mod_security
обнаруживает и предотвращает атаки, проводимые как с использованием
GET, так и POST. Возвращаясь назад, добавление возможности контроля
POST запросов было большой проблемой для Apache 1.3.x, так как он не
поддерживал фильтры.


Установка и конфигурирование
Наилучшем вариантом установки mod_security является компиляция его из
исходных текстов (или, если вы используете Windows и у вас нет
компилятора, то посетите сайт и скачайте оттуда уже скомпилированные
модули):

$ /path/to/apache/bin/apxs -cia mod_security.c
# /path/to/apache/bin/apachectl stop
# /path/to/apache/bin/apachectl start

Перед тем как начать создавать правила, добавьте несколько строк в
файл конфигурации:

<IfModule mod_security.c>

# Включает или выключает движок фильтра
SecFilterEngine On

# Проверка правильности кодирования URL
SecFilterCheckURLEncoding On

# Проверка UNICODE кодирования
SecFilterCheckUnicodeEncoding Off

# Использовать только байты из этого диапазона
SecFilterForceByteRange 0 255

# Вести лог только для подозрительных запросов
SecAuditEngine RelevantOnly

# Имя файла лога
SecAuditLog logs/audit_log
# Вывод отладочной информации (установлен минимальный уровень)
SecFilterDebugLog logs/modsec_debug_log SecFilterDebugLevel 0

# Осуществлять проверку POST запросов
SecFilterScanPOST On

# Для подозрительных запросов по умолчанию писать в лог
# и возвращать HTTP ответ с кодом 500
SecFilterDefaultAction «deny,log,status:500»

</IfModule>

Я оставил комментарии в тексте, поэтому назначения директив вполне
очевидно. Данная конфигурация активирует mod_security, но больше
ничего не делает. Всегда желательно начинать с простой конфигурации и
добавлять в нее все необходимое.



Так что он может делать?
Даже с такой простой конфигурацией, mod_security предоставляет две
существенные выгоды. Он выполняет несколько техник, предназначенных
для предотвращения скрытых угроз, а также унифицирует весь входящий
поток. Позже это поможет, когда мы начнем добавлять правила фильтрации
в конфигурацию сервера. Представьте, что вы хотите запретить людям
выполнять на сервере бинарный файл ps, используя регулярное выражение,
такое как /bin/ps ax. Это выражение будет отлавливать простые
обращения, но не /bin//ps ax или /bin/ps%20ax, или /bin/./ps ax. Вот
список того, что mod_security выполняет:

* Удаляет множественные слеши (//)

* Удаление директории, ссылающихся на самих себя (./)

* Обрабатывать одинаково и «„, и „/“ (только для Windows)

* Производить декодирование URL

* Заменять пустые байты () пробелами.

Также выполняется ряд проверок:

* Проверка URL кодирования.

* Проверка UNICODE кодирования.

* Проверка байтового диапазона, где допускаются только определенные
значения символов части запроса.


Действия
Всякий раз, когда срабатывает правило  — выполняется некоторая
последовательность действий. Список действий по умолчанию (задается
директивой SecDefaultAction) выполняется в большинстве случаев, но
также возможно настроить действие на каждое правило с помощью второго
аргумента в SecFilter или третьего аргумента в SecFilterSelective. Вот
список возможных действий:

* deny, отвергает запрос.

* allow, останавливает проверку правил и допускает запрос.

* status:nnn, возвращает HTTP ответ с кодом nnn.

* redirect:url, направляет запрос на абсолютный URL url.

* exec: cmd, выполняет скрипт cmd.

* log, запись запроса в лог ошибок.

* nolog, не вносить запрос в лог.

* pass, пропустить текущее правило и перейти к следующему

* pause:nnn приостановить обработку запроса на nnn миллисекунд.
Будьте очень осторожны с этим действием. Apache остается занятым
при остановке обработки. Вы фактически можете помочь
злоумышленникам провести DoS-атаку.

Следующие действия влияют на порядок выполнения правил, подобно тому,
как работает mod_rewrite:

* chain Следующее правило в цепочке. Если правило в цепочке не
срабатывает, то, следующие за ним, правила пропускаются.

* skipnext:n пропустить следующие n правил.


Правила фильтрации
Правила бывают двух видов. В простейшей форме:

SecFilter keyword

проверяет наличие keyword (регулярное выражение) в первой строке
входящего запроса (которая выглядит как GET /index.php HTTP/1.1) или в
теле POST если оно есть. Вместо него вы можете использовать:

SecFilterSelective „произвольный список разделенный |“ keyword

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

Данное правило пропустит все запросы от одного IP адреса (с моей
рабочей станции). Другие правила обрабатываться не будут. Так как
данные запросы не несут злого умысла, поэтому они не попадают в лог:

SecFilterSelective REMOTE_ADDR „^IP_ADDRESS_HERE$“ nolog,allow

Данное правило позволяет мне получить полный доступ из моего ноутбука,
когда я в дороге. Так как я не знаю, какой IP адрес у меня будет,
доступ гарантируется всем клиентам, содержащим строку „Blend 42“ в
поле User-Agent. Это слабая защита, но достаточно интересная, как
метод идентификации.

SecFilterSelective HTTP_USER_AGENT „Blend 42“

Данное правило предотвращает SQL инъекции в cookie. Если существует
cookie, запрос выполняется только в том случае, если cookie состоит из
цифр.

SecFilterSelective COOKIE_sessionid „!^ (|[0-9]{1,9})$“

Данное правило требует HTTP_USER_AGENT и HTTP_HOST заголовки в каждом
запросе. Злоумышленники часто работают, используя простые средства (в
т.ч. telnet) и не посылают всех заголовков, которые отсылает браузер.
Такие запросы будут отвергнуты, помещены в лог и проконтролированы.

SecFilterSelective „HTTP_USER_AGENT|HTTP_HOST“ „^$“

Данное правило запрещает закачку файлов на сервер. Это простая, но
эффективная защита; отказ запросов основан на типе контента,
используемого при закачке файлов.

SecFilterSelective „HTTP_CONTENT_TYPE“ multipart/form-data

Это правило заносит в лог запросы без заголовка «Accept“ для
последующей его проверки; Ручные запросы часто не содержат всех HTTP
заголовков. Заголовок «Keep-alive» другой хороший кандидат на
проверку.

SecFilterSelective «HTTP_ACCEPT» «^$» log,pass

Это правило отсылает мне письмо всякий раз, когда начальник вновь
забывает свой пароль. Тут у нас два правила. Первое срабатывает
только, когда запрошен определенный файл (в данном случае файл,
отображающий сообщение о неверном пароле). Второе правило проверяет
имя пользователя на соответствие «ceo». Если это так, то выполняется
внешний скрипт:

SecFilterSelective REQUEST_URI «login_failed.php» chain
SecFilterSelective ARG_username «^ceo$»
log,exec:/home/apache/bin/notagain.pl

Данное правило отправляет обратно на сайт Google робота Googlebot,
используя заголовок User-Agent. Данный запрос не попадает в лог.

SecFilter HTTP_USER_AGENT «Google»
nolog,redirect:http://www.google.com

Данное правило проверяет все переменные JavaScript, пропуская
переменную с именем html. Другие же переменные JavaScript отвергаются,
так как они могут вызвать ошибки в некоторых приложениях (актуально
для CMS средств).

SecFilter «ARGS|!ARG_html» «<[:space:]*script»

В завершении, данный пример покажет, как использовать множественную
настройку mod_security. Это означает, что можно создавать правила для
определенных приложений. Запомните директиву SecFilterInheritance. C
ее помощью мы сообщаем mod_security, что необходимо игнорировать все
правила родительского контекста.

<Location /anotherapp/>

SecFilterForceByteRange 32 126
# Использование этой директивы НЕ игнорирует правила родительского контекста
SecFilterInheritance Off

# Разработчики часто используют специальные переменные
# для работы в Debug режиме. Эти два правила
# разрешают использовать переменную «debug»
# но только для внутренней сети
SecFilterSelective REMOTE_ADDR «!^192.168.254.» chain
SecFilterSelective ARG_debug «!^$»

</Location>

Производительность
У меня никогда не было проблем с производительности при использовании
mod_security. В моих тестах производительности разница примерно
составляла 10 процентов. Тем не менее, на практике производительность
немного падает. На реальных веб-сайтах, один запрос страницы может
вызвать множество запросов на графику, стили и JavaScript библиотеки.
Mod_security не рассматривает дочерние запросы, только если это не
задать явно:

SecFilter DynamicOnly

Узким местом всегда были операции ввода/вывода. Убедитесь, что на
работающем сервере выключен режим отладки, и используйте максимальный
уровень ведения лога, только тогда, когда он вам действительно нужен.
В конфигурации представленной выше лог ведется только для «уместных»
запросов, т.е. тех, которые приводят к срабатыванию фильтра.


Другие возможности

Внутренний chroot
Если вам когда-нибудь приходилось использовать chroot для веб-сервера,
то вы, вероятно, знаете какая это сложная задача. Для mod_security эта
сложность исчезает. Достаточно использовать одну директиву:

SecChrootPath /chroot/home/web/apache

Единственное требование, это чтобы путь сервера в chroot был подобен
обычному пути (например: /home/web/apache).


Подмена сигнатуры сервера
Злоумышленники и автоматические скрипты для сбора данных о сервере и
его версии часто используют HTTP-заголовок «Server», который
отсылается с каждым запросом. Вы можете изменить ее путем внесения
изменения в код Apache, но также это можно сделать, используя
следующую директиву. (Вы можете воспользоваться этой возможностью
только для Apache 1.x. Модуль mod_headers включенный в Apache 2.x
перехватывает исходящие заголовки и меняет их «на лету»):

SecServerSignature «Microsoft-IIS/5.0»

Ссылки
mod_security
Short