Защита для NGINX — NAXSI

    Что такое NAXSI ?


    NAXSI = NGINX ANTI XSS & SQL INJECTION
    Проще говоря, это файрвол веб-приложений (WAF) для NGINX, помогающий в защите от XSS, SQL-инъекций, CSRF, Local & Remote file inclusions.
    Отличительными особенностями его являются быстрота работы и простота настройки. Это делает его хорошей альтернативой например mod_security и апачу.

    Зачем нужен NAXSI ?

    Очевидно, лучше всего защищаться от вышеперечисленных атак правильно написанным кодом. Но есть ситуации, когда WAF (и в частности naxsi), поможет:
    • Низкое качество кода сайта, при отсутствии возможности/ресурсов все выкинуть и переписать нормально.
    • “Закрытый” код, в котором невозможно исправить ошибки.
    • Неизвестное качество кода в важном для бизнеса участке.



    Установка

    Ubuntu, Debian, Netbsd, Freebsd: доступно в виде пакета.
    Например, в серверной убунте 12.04 достаточно сделать
    apt-get install nginx-naxsi 
    


    Другие Linux-системы:
    Если пакеты еще не появились, собираем nginx + naxsi из исходников:
    wget http://nginx.org/download/nginx-x.x.xx.tar.gz
    wget http://naxsi.googlecode.com/files/naxsi-x.xx.tar.gz
    tar xvzf nginx-x.x.xx.tar.gz
    tar xvzf naxsi-x.xx.tar.gz
    cd nginx-x.x.xx/
    

    (вместо x.x.x.x — поставьте актуальные версии)

    Проверяем, что есть зависимости libpcre (опционально, libssl для https), и компилим:
    ./configure --add-module=../naxsi-x.xx/naxsi_src/  [тут ваши опции для nginx]
    make
    make install
    


    Как работает NAXSI

    NAXSI умеет проверять по наборам правил GET-запрос, заголовки HTTP (например, cookies) и тело POST-запроса.
    Базовый набор запретительных правил довольно простой и запрещает различные “опасные” символы и ключевые слова sql.
    Этот набор правил достаточно жесткий, и может помешать корректной работе сайта в некоторых случаях, поэтому в NAXSI реализованы “белые” списки, разрешающие использовать запрещенные символы (правила) в нужном вам контексте.

    При проверке запроса, он прогоняется по всем запретительным правилам, за исключением помещенных в белые списки для его контекста, и производится подсчет “штрафных” очков по шести категориям: $SQL, $XSS, $RFI, $TRAVERSAL, $EVADE, $UPLOAD.

    Если количество “штрафных” очков выше порогового уровня, запрос считается опасным и производится внутренний (для nginx) redirect на DeniedUrl, указанный в конфигурации. В виде get-параметров на указанный url передается исчерпывающая информация о причине блокировки, оригинальном url и ip агрессора. По указанному адресу вы можете или просто выдавать return 403; или же накапливать информацию об атаке в вашей NIDS-системе.
    NAXSI умеет работать в “обучающемся” и “боевом” режимах.

    В обучающемся режиме, NAXSI сам может подготовить для вас набор “белых” списков, основываясь на активности пользователей. Проще говоря, если пользователи часто нарушают одно из правил по одному и тому же URL — правило вносится в белый список, и не блокируется. Эти списки стоит просмотреть и откорректировать после окончания обучения.

    В боевом режиме нарушение просто ведет на DeniedUrl.

    Настройка NAXSI

    Раскомментируем в конфигурации nginx включение базовых запретительных правил
    include /etc/nginx/naxsi_core.rules;
    


    Теперь добавим в конфигурацию виртуального хоста желаемые настройки (рекомендую вынести их в отдельный файл и подключать через include):
    LearningMode;
    SecRulesEnabled;
    DeniedUrl "/RequestDenied";
    
    #include "/etc/nginx/mynaxsi.rules";
    
    ## check rules
    CheckRule "$SQL >= 8" BLOCK;
    CheckRule "$RFI >= 8" BLOCK;
    CheckRule "$TRAVERSAL >= 4" BLOCK;
    CheckRule "$EVADE >= 4" BLOCK;
    CheckRule "$XSS >= 8" BLOCK;
    


    Разберем подробней, что значат эти команды:
    • LearningMode — включен режим обучения. Запросы не блокируются, формируется вайт-лист.
    • SecRulesEnabled — NAXSI включен для данной локации. Если захотим выключить для другой локации (например, защищенной внутренней зоны), то делаем в ней SecRulesDisabled.
    • DeniedURL — URL редиректа для запрещенных запросов.
    • CheckRule — проверка “штрафных очков” запроса по категориям.
    • /etc/nginx/mynaxsi.rules — сгенерированные правила (пока не сгенерили — закомментированные).

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

    Как работать с правилами и списками, а также смотреть статистику и бенчмарки, я расскажу в следующей статье.

    Если вас заинтересовал NAXSI, вы можете ознакомиться с хорошей документацией на Wiki проекта.
    Share post

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 60

      +5
      Интересное решение для ситуации, когда требуется хостить подозрительные сайты.
        +1
        В качестве альтернативы можно попробовать Nemesida WAF Free — полностью бесплатный, представлен в виде динамического модуля Nginx, устанавливается и обновляется из репозитория, не требует компиляции, подключается за несколько минут к уже установленному Nginx (начиная с версии 1.12). waf.pentestit.ru/about/2511
        +4
        Используется-ли данный модуль на продакшене? Как долго? При какой нагрузке?
          +5
          Да, используется, нагрузка на продакшене мала, но были проведены тесты/бенчи при нагрузке через ab.
          Подробности в след статье, ок?
        +1
        Хм, для фряхи в nginx-devel модуль есть, в стабильной пока нет. Пойду поставлю devel, очень уж хочется потестировать модуль:)
          +1
          Вот только хотел статью опубликовать про naxsi!
          Ну что ж, спасибо, информация нужна, а кто не успел, тот опоздал)
            +2
            $SQL и $XSS — для магазина по продаже подгузников это сработает. Для сайта типа Хабра — по очевидным причинам — нет.
            $RFI — ну, тут, скорее всего, можно сделать достаточно надёжный набор правил.
            $TRAVERSAL — здесь тоже 100% попадание — модуль идеально подходит для этой задачи. Главное поисковики не забанить
            CSRF — проверка реферера, с теми же дырами, что и обычно.

            Так что я бы всё-таки подчеркнул разницу между «поможет защитить» от «защищает». Особенно на «важном для бизнеса участке».
              +2
              Да, это не серебряная пуля, но при грамотной настройке может помочь.

              $SQL и $XSS категории вполне можно применить для хабра, указав в каких полях может быть sql и код. На то и нужны вайтлисты с возможностью разбивки по зонам.
                0
                Я думаю, что Вы ошибаетесь. Можно захаркодить правила наверное для 90% случаев, просто нужен опыт в этом деле хороший.
                  +2
                  Можно.
                  Но решение ведь явно из разряда «поставил и забыл».
                  А при наличии таких ресурсов, как время и опыт, я бы всё-таки потратил их на написание безопасной версии сайта, а не на паллиатив.
                  Так что модуль явно для «ситуации, когда требуется хостить подозрительные сайты» которые не жалко, а времени заниматься ими нету.
                    0
                    Да, согласен, время идёт и много нового. Но, настройкой модуля и разработкой сайта всё таки могут и _должны_ заниматься разные люди. Поэтому настроить и обновлять модуль не является большой проблемой через репозиторий. Даже считаю, что уже необходимо такой создать, где люди могли бы совместными усилиями составлять и дополнять правила в конфиге/конфигах под разные ситуации.
                      0
                      Ой, вот уж чему-чему, а «коллективному разуму» я безопасность своего сайта тем более не доверю. У семи нянек — дитя без глазу.
                      А честные посетители получают Forbidden за смайлик и пример SQL запроса в комментарии.
                +1
                По описанию сладко… Нужно пощупать ручками.
                Спасибо!
                  +2
                  Я давно наблюдаю появившийся nginx-naxsi в менеджере пакетов Synaptic, чуток описание прочитал и решил отложить его использование на попозже когда будет время с ним разобраться и узнать получше — вот и пришло время для этого.
                    –1
                    Такие модули заставляют девелоперов писать некачественный код, полагаясь на ВАФ. :)
                      +6
                      Прямо таки «заставляют»?

                      Они бы и рады писать хорошо, но раз есть NAXSI — будут писать плохо?
                        +1
                        Ну, здесь, мне кажется, мы говорим о гипотетических сайтах с «ужасным» legacy code.
                        Если говорить о новых разработках, то уж читатели-то хабра должны бы уже забыть о таких простых атаках, как
                        — SQL-инъекция (2 простых правила для написания безопасного кода)
                        — CSRF (1 правило)
                        — LFI/RFI/TRAVERSAL (два)
                        с XSS сложнее, но уж на уровне-то этого модуля тоже решение элементарное. Которое, к тому же, не будет за смайлики 403 показывать.
                          0
                          Могли бы вы написать эти простые правила?
                            0
                            Хм, да было бы интересно услышать, только если это не всем известные правила, вида CSRF: POST — изменение, GET — просмотр, ну и если это не правила по использованию тех или иных вещей в php и его конфигах, т.е. если это не привязано строго к ЯП.
                              +1
                              Наверное так:
                              1.1 Строковые значения в SQL запросах экранировать (с учётом параметров текущего соединения)
                              1.2 Числовые значения или приводить к числу, или проверять на то, что это числа
                              2 Генерировать и проверять токен в формах на изменение данных, ессно формы POST
                              3 Тут затрудняюсь сказать что конкретно имелось в виду, но в общем не использовать в файловых функциях что попало, даже полученное из БД или файлов доступных на запись, куда вроде бы сами положили. По возможности использовать хэши (include $pages[$_GET['page']] . '.php';, где $pages['some_page'] == 'some_page', а не include $_GET['page'].'.php'; для php) или другой вариант «белого списка» имён файлов. Если невозможно — фильтровать ввод на спецсимволы вроде точек и слэшей или проверять, что реальные пути запрашиваемых файлов не выходят за отведенные им каталоги или другой вариант белого списка каталогов, причём сравнивать обязательно реальный путь, вычисленный уже осью, а не то, что пришло извне.
                              4 (XSS) экранировать вывод (не забывать что для HTML нужно одно экранирование, а для JS(ON) другое)
                                0
                                Ну это стандартно дефакто понятно все :) жду все же может раскроют «тайны» какие-либо) кстати, токен тоже не особо помогает при CSRF )
                                  0
                                  Так я ж и не писал про какие-то секретные знания. :)
                                  Наоборот, исходил из того, что читателям хабра эти правила известны.

                                  Можно про CSRF своими словами или ссылкой?
                                    0
                                    лучше ссылкой, я не спал уже полтора дня, поэтому словами будет тяжело :) href=«www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) (очень много, но подробно, думаю эта ссылка вам известна скорее всего), а второе это вот это www.adambarth.com/papers/2008/barth-jackson-mitchell-b.pdf тоже разобраны способы различные, их плюсы и минусы :)
                                      0
                                      Ну, по обеим ссылкам пишут, что токен-то как раз работает, просто его забывают использовать (для формы авторизации, например).
                                        0
                                        Хм, да основная проблема состоит именно при авторизации, когда собственно и происходит генерация токена, проблемы с токеном могут вскрыться потом, например при AJAX(если не хранить один и тот же токен для всей сессии, а сбрасывать его при каждом пост запросе), плюс если почитаете ссылку вторую до конца там тоже описываются ситуации другие.
                                          0
                                          А можно, всё-таки, своими словами?
                                          «Другие ситуации», которые я там увидел — это другие типы атак, man in the middle, например.
                                          Но подтверждения фразе «токен тоже не особо помогает при CSRF» я там не увидел.
                                            0
                                            Ну например, приложение находится на нескольких доменах это уже значит что токен приходится хранить в куки(недостаток существенный), если у вас сессия не в БД конечно, хотя и это накладывает свой гемор, дальше как я уже указал, если сбрасывать каждый раз после post-запроса например, то это уже проблемы с ajax и прочими подобными вещами. Скомпрометировать те же куки и узнать токен ведь проще(неважно как, сейчас не рассматриваем как украсть куки) на стороне клиента, чем как-то на стороне сервера его подделать? С другой стороны можно было бы анализировать реферер но и он может быть подделан(в ссылках описано), поэтому ни то ни то не спасает полностью, и приходится выбирать в зависимости от нужд. попытался описать кратко, не ввязываясь в особую демагогию :)
                                              0
                                              Нет-нет, токен в куках — это не «недостаток», это просто фейл.
                                              Куки в принципе не могут защитить от CSRF, исходя из самой природы этой атаки: зачем красть куку, если браузер сам её отправит? :)

                                              Не вижу, кстати, причины, по которой токен в коде формы не будет работать на мультидоменной конфигурации. Чем токен отличается от любой другой сессионной переменной?
                                              Если у нас есть требующее сквозной авторизации приложение, работающее на нескольких доменах, то сессия по определению должна работать на них на всех.
                                                0
                                                Я же описал выше, что сессию придется кидать не в «файлы», а как минимум в БД, что естественно накладывает и свои ограничения, специфические для реализации сессии через БД(так что ваш вопрос звучит немного непонятно), особенно когда несколько серверов, вообщем это долгая тема. По поводу куки кстати, в Yii так сделано :) там токен генерируется 1 раз и ставится в куки, потом из них забирается если что, решение сомнительное но все же :)
                                                  0
                                                  Да какая разница, куда «кидать» сессию?
                                                  К защите от CSRF это не имеет отношения.
                                                  Сессия может прекрасно жить и в файлах, если «несколько доменов»находятся на одном физическом сервере. Может «кидаться» в мемкеш даже если домен только один. может в базу. Но к теме вопроса это не имеет ни малейшего отношения.

                                                  В общем, чтобы определиться.
                                                  1. Сессия тут вообще не при чём. Она у нас уже есть, и как она реализована — нам не важно.
                                                  2. В куках токен хранить не имеет смысла:
                                                  — если использовать его по прямому назначению — проверяя его наличие при обработке формы — то для защиты от CSRF это не поможет.
                                                  — просто класть в куки, «а потом забирать, если что» так же бессмысленно: злоумышленник преспокойно получит токен себе и впишет его в форму. Смысл имееет только пара «токен в сессии — токен в коде формы»

                                                  Так я до сих пор и не увидел примера ситуации, в которой сохраняемый одновременно и в сессии, и в форме токен «не особо помогает при CSRF.»

                                                    0
                                                    Блин, я вам в предыдущих сообщениях все подробно расписывал и ссылки приводил, а у вас на любой мой ответ только одно «не вижу доводов», ну круто че. Вообщем лучше закрыть тему, а то это будут взаимные притензии и оскорбления еще не дай бог :) каждый при своем :)
                                                      0
                                                      Ясно. По остальным пунктам возразить нечего.
                                                      Вот и славненько.
                                                        0
                                                        ну остальные пункты почти «аксиомы» базовой безопасности приложения, если кто-то не соблюдает их, то сам виноват.
                                                          0
                                                          По пунктам моего комментария выше.
                                                          В нём объясняется, почему твои «подробно расписанные сообщения» не имеют никакого отношения к вопросу, который был тебе задан.

                                                          Возразить на эти объяснения тебе нечего.
                                                          Что, с одной стороны жаль — я ожидал большей адекватности от собеседника — но с другой сильно облегчает мою задачу: мне больше не нужно тратить своё время.
                                    0
                                    уж читатели-то хабра должны бы уже забыть о таких простых атаках, как

                                    Стандартно, но, наверное, подавляющая часть сайтов с уязвимостями эти правила не соблюдает. Тут не до тайн :)
                                    0
                                    Мда, видимо, правила всё-таки не настолько очевидные, если для SQL совсем забыты идентификаторы и ключевые слова :)
                                    RFI/LFI — remote/local file execution, directory traversal — переход на уровень выше. Чтение и вывод файлов, в общем.
                                    Мне кажется, что правила для работы с файлами могут быть куда проще, для этого даже белый список не нужен.
                                    Я вообще всегда стараюсь упрощать наборы правил, а то длинные соблюдать сложно :)

                                    Что интересно — правила разработки отличаются от правил фильтрации. Первые одновременно и проще, и надёжнее. Именно этим фактом вызвано моё скептическое отношение к таким файрволлам.
                                    Если, скажем, фильтратору важно знать, что в переданном запросе есть точки, то он даже невинные зарубит, а обращение, скажем, к абсолютному пути прекрасно пропустит — там точек нету ведь! :)
                                    А разработчик может просто выделить в переданном параметре имя файла, каталог сгенерировать самому и проверить получившийся путь на читаемость.
                                      0
                                      Про идентификаторы и ключевые слова не понял. Когда в урлах или полях поста передаются названия таблиц и/или полей, а то и UPDATE/INSERT/...?
                                        0
                                        Да, верно. Только речь не только об урлах, а о динамике в запросах вообще.
                                        И в качестве примера ключевого слова я бы привёл скорее такие, как OR или DESC.
                                        0
                                        А про RFI/LFI/Traversal я и написал, что или точный белый список файлов, или белый список каталогов по real path. С правилами сложно всё предусмотреть, по-моему, чтобы сделать универсальный фильтр, который из проекта в проект таскать.

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

                                          Для случаев, когда приложение занимается именно просмотром физических каталогов по запросам пользователя, подойдёт описанное выше решение-аналог open_basedir.
                                            0
                                            Я больше про довольно удобный вариант когда у каждого юзера своя «домашняя» папка.
                                              0
                                              А пользовательские-то файлы здесь при чём? Мы же, вроде, говорили о защите файлов приложения/сервера?
                                              Я не очень понял сценарий атаки с пользовательской папкой.
                                                0
                                                Типа пользователькие файлы аплоадятся к нему в паку, а потом доступ к ним по урлу. Вроде example.com/image.php?path=user/album/photo.jpg

                                                В таких случаях беру real path от UPLOAD_DIR . $_GET['path'] и смотрю чтобы не вышел за UPLOAD_DIR Если не смотреть, то можно /etc/passwd получить вместо фотки :)
                                    +2
                                    Речь о правилах разработки, а не фильтрации, если что :)

                                    SQL: данные в запрос передавать только через плейсхолдеры. Идентификаторы и ключевые слова — только фильтруя по белому списку, своему для каждого запроса.

                                    CSRF: привязанные к сессии токен в каждой POST форме.

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

                                    XSS — htmlspecialchars принудительно внутри шаблонизатора для всех переменных, кроме, опять же, специально помеченных в белом списке.

                                    Такие очевидные правила, как «ни одна переменная не выводится мимо шаблонизатора» или «никакое действие, меняющее состояние сервера, не выполняется методом GET » я не выделяю отдельно, хотя, возможно, стоило бы.
                                      0
                                      ну вообщем вы перечислили правила «де факто», которые 90 % норм. разработчиков знают :) ну и на этом спасибо)
                                        0
                                        А как показывает практика разработчики 90% сайтов с уязвимостями… 90% сайтов делают 10% разработчиков, которые эти факты не знают? :)
                                        0
                                        Повторение — мать учения, поэтому спасибо)
                                  +6
                                  Пользователи gentoo могут проголосовать за включение naxsi в ебилд nginx тут.
                                    0
                                    Есть ли уже готовый пример сайта? Хотелось бы посмотреть ни сиё чудо.
                                      +4
                                      И как же должен со стороны выглядеть сайт, прикрытый web application firewall? Чем отличаться от остальных?
                                        +1
                                        скорее всего уважаемый Asperka хотел увидеть работающий конфиг для «реального» сайта.
                                      0
                                      Вот наставят такого, еще всяких сухосинов, а что толку? Все равно если приложение подвержено, то найдется способ провести атаку.
                                        0
                                        Сухосин не плохо фильтрует левые запросы. Зачем их обрабатывать даже если атака не пройдет? — это лишняя нагрузка. Тут то же самое, но уже на более низком уровне (на уровне веб сервера) — фильтр левых запросов, — лишним не будет при правильной настройке.
                                          0
                                          Чтобы такой фильтр («ненужных» запросов) был эффективным, к нему нужна ещё дополнительная банилка, скажем, на час.
                                            0
                                            Нам баннилка не нужна, т.е. нам хватает того, что левые запросы отсекаются просто, если вы считаете, что это эффективно или в вашем случае это необходимо, можно и баннилку прикрутить — это не проблема. Я просто возразил на то, что от подобных фильтров нет толку. Если
                                            у вас сайт посещает 200 человек в сутки, толку, конечно, мало. Если же у вас проект работает под нагрузкой, дополнительный фильтр «левака» совсем не помешает. То есть, я рассматриваю это именно как фильтр, а не как защиту на которую нужно уповать, — понятное дело сухосин не спасет от нормальных хакеров.
                                              0
                                              :)
                                              Как раз о снижении нагрузки и идёт речь.
                                              Вы забываете, что здесь используется чёрный список.
                                              То есть, он по определению не может охватить все «паразитные» запросы.
                                              При этом мы можем предположить, что если с определённого IP пришёл запрос на /etc/passwd, то в течение ближайшего (условно) часа от него вряд ли можно ожидать покупок, и с чистой совестью можно банить.
                                              А отсекать 1 запрос по известному системе паттерну и пропускать 99 столь же «полезных», но валидных с точки зрения файрволла — смысла немного.
                                              Впрочем, вы правы. Если у вас посещаемость не 200 человек в сутки, хакерская активность не слишком заметна на фоне обычного трафика.
                                                +1
                                                >При этом мы можем предположить, что если с определённого IP пришёл запрос на /etc/passwd, то в течение ближайшего (условно) часа от него вряд ли можно ожидать покупок, и с чистой совестью можно банить.

                                                Как же бесит, когда из-под провайдерского или рабочего NAT заходишь, а там что-то вроде «Hacker, your are catched. he-he»
                                        0
                                        Кто-нибудь пробовал использовать предложенный в naxsi-0.46-1 lerning mode?
                                        code.google.com/p/naxsi/wiki/LearningMode0_46 — в комментах жалуются на то, что после формирования база остается пустой. Хотя эксепшны в логах nginx'а есть.
                                          0
                                          Блин. Что-то я вот тоже попробовал эту хрень — хотел поставить на сервер това'ища, который наплодил у себя портальчиков на вордпрессе и которые ему раз в неделю где-то ломают. Хотел сначала отрубить все правила и оставить только одно — запрет загрузки *.php.
                                          Убрал вообще все MainRule, кроме одного, убрал все CheckRule, кроме одного, соответствующего этому MainRule.
                                          А оно всё равно блокирует многие запросы. Причём вообще не понятно, по какому принципу:

                                          2013/06/27 18:43:44 [error] 19870#0: *345 NAXSI_FMT: ip=111.253.0.43&server=XXXXXXXX.ru&uri=/wp-login.php&total_processed=30064771079&total_blocked=22333829940, client: 111.253.0.43, server: XXXXXXXX.ru, request: «POST /wp-login.php HTTP/1.0», host: «XXXXXXXX.ru», referrer: «XXXXXXXX.ru/wp-login.php»
                                          2013/06/27 18:43:44 [error] 19870#0: *348 NAXSI_FMT: ip=94.127.70.77&server=XXXXXXXX.ru&uri=/wp-cron.php&total_processed=8589934594&total_blocked=12025908428, client: 94.127.70.77, server: XXXXXXXX.ru, request: «POST /wp-cron.php?doing_wp_cron=1372344224 HTTP/1.0», host: «XXXXXXXX.ru»
                                          2013/06/27 18:43:45 [error] 19870#0: *353 NAXSI_FMT: ip=24.139.92.197&server=XXXXXXXX.ru&uri=/wp-login.php&total_processed=34359738376&total_blocked=22333829940, client: 24.139.92.197, server: XXXXXXXX.ru, request: «POST /wp-login.php HTTP/1.0», host: «XXXXXXXX.ru», referrer: «XXXXXXXX.ru/wp-login.php»

                                          Где здесь вообще информация о применённом правиле?..

                                          Only users with full accounts can post comments. Log in, please.