Пользователь
13,6
рейтинг
9 февраля 2013 в 12:58

Разработка → На пути к созданию безопасного веб-ресурса. Часть 1 — серверное ПО tutorial

Я уже довольно долгое время хочу формализовать все свои мысли, опыт, ежедневно применяемый на практике, и многое другое в одном месте и предоставить их общественности. Уверен, многим этот материал будет полезен. Он посвящен различным моментам в конфигурации серверного ПО Linux и безопасным подходам к созданию сайтов/приложений на php (все же это до сих пор одна из самых популярных связок, хоть её успешно и подвигают другие технологии. Но советы так же легко применимы и к веб-ресурсам на других технологиях).

Т.е. речь идет о типичной ситуации. Проект (стартап), купили под него сервер и разворачиваем на нем сайт. Бизнесу не нужно тратить лишних денег на сервера (поэтому будут выбраны наиболее производительные связки ПО), а так же нужно, чтобы все было безопасно, при чем бесплатно :)

Конфигурация серверного ПО


Nginx + php-fpm

Начал бы я с объяснения выбранной связки. У меня были различные ресурсы, довольно высоконагруженные, где нужно было уживаться сразу нескольким приложениям, а кроме того, не только вебу. Я перепробовал всевозможные связки (apache, apache с отдачей статики nginx'ом, nginx + php в режиме cgi и т.д.) и наилучшим вариантом все-таки оказалась связка nginx + php-fpm. Как по стабильности, так и по гибкости настройки, так и в плане безопасности. Ну а если у вас до сих пор Apache, то прекратите им пользоваться прямо сейчас (С) не помню откуда (хотя следующие советы легко портируются и подходят и для любой другой связки).
Прежде всего хотел бы сказать, что стоит разделять машины для каждого ресурса. Да, можно каждый сайт изолировать друг от друга, как это делается на shared-хостингах, но все же сейчас аренда виртуальных серверов не так высока (задумайтесь над этим, прежде чем разместить еще один сайт на этой же машине, пусть даже соблюдая различные правила: разные юзеры для каждого ресурса и т.д. Ну или пытайте jail ;).

Начнем с конфига самого сайта:

location /index.php {
...
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.socket;
...
}

Желательно спроектировать веб-приложение так, чтобы оно могло работать всего от одного php-файла в htdocs и подобным образом настроить location — /index.php. Это исключает вообще возможность выполнения залитого php-шелла в htdocs. Мы вернемся к этому во второй части этой статьи.

Фиксим багу с fix_pathinfo, описанную тут. Редактируем php.ini и меняем значение параметра fix_pathinfo на

cgi.fix_pathinfo=0

Усложняем жизнь скрипт-кидди и блочим популярные сканеры по UA (из статьи):

if ( $http_user_agent ~* (nmap|nikto|wikto|sf|sqlmap|bsqlbf|w3af|acunetix|havij|appscan) ) {
    return 403;
}

Верьте мне, львинная часть «хакеров» отсеется :)

Костыль для защиты от CSRF

Следующее правило я крайне не рекомендую к использованию, но если столкнулись с проблемой, что кто-то активно юзает CSRF на ваших юзерах следующее правило может помочь, если нет никакой нормальной возможности добавить защиту от CSRF

# ANTI CSRF HACK
    valid_referers blocked example.com www.example.com;
    if ($invalid_referer) {
	set $possible_csrf 1;
}
    if ($request_method = POST) {
	set $possible_csrf "${possible_csrf}2";
}
    if ($possible_csrf = 12) {
	return 403;
}

Подобные страшные конструкции нужны, так как nginx не поддерживает множественные условия в if, а так же не поддерживает вложенные if'ы. В первой строчке указываем реферы, которые будут валидны (blocked — порезанные фаирволлом и реферы с нашего сайта). И если метод = POST, то блочим пользователя. В наше время почти никто не использует прокси-сервера, которые вырезают referer в запросах клиентов, но такая вероятность есть. Поэтому пример выше — не решение, а лишь грязный хак.

Anti-DNS pinning

Правила разбора host нужно настроить так, что если значение HOST в HTTP заголовке не нашего ресурса, то отдавать 404. Пример.

X-Frame-Options

Добавляем жизненно необходимые заголовки (так же почерпнем информации из статьи VladimirKochetkov). Во-первых, запретим показ нашего ресурса в iframe:

add_header X-Frame-Options DENY;

Это спасет наш ресурс от возможного DDOS'a через iframe, а так же от возможного clickjacking'a на сайте. Не стоит забывать и про трюки с CSS с использованием iframe.

X-Content-Type-Options

add_header X-Content-Type-Options nosniff;

Это скажет IE, что нет необходимости автоматически определять Content-Type, а необходимо использовать уже отданный content-type. Уже были security-баги у IE, связанные именно с автоматическим определением типа содержимого.

X-XSS-Protection

add_header X-XSS-Protection "1; mode=block;";

Так же заголовок для IE. Активирует встроенную XSS-защиту.

X-Content-Security-Policy

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

add_header X-Content-Security-Policy "allow 'self';";
add_header X-WebKit-CSP "allow 'self';";

Определяет, с каких доменов можно подгружать JS (X-Content-Security-Policy для IE10 и X-WebKit-CSP для FF/Chrome). В примере выше указано правило, которое позволит подгружать JS только с этого же домена.

Strict-Transport-Security

Если Ваш ресурс работает через https и происходит редирект с 80го порта на 443 (для удобства) то клиент может поддерживать некоторое время незащищенное соединение. Данный заголовок исключает возможность MITM в этот самый, довольно короткий, промежуток времени (перехват трафика в TOR). Подробнее здесь.

Firewall

Одна из важнейших конфигураций. Общая идея такая — разрешаем несколько, запрещаем все остальное. Я не гуру iptables, возможно кто-нибудь в комментариях меня поправит и мы придем к более лучшему решению. Считаем, что eth0 у нас внешний интерфейс и будем ограничивать именно его (обычно доступ из внутренней сети полный).

-A INPUT -i eth0 -p icmp -j ACCEPT # разрешаем пинг до нашего ресурса
-A INPUT -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT # разрешаем только уже поднятые или порождаемые соединения
-A INPUT -s 12.34.56.78 -i eth0 -j ACCEPT # здесь указываем IP адрес, с которого бы нам желательно иметь полный доступ ко всем портам (если это требуется)
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT  # ssh
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT # web
-A INPUT -i eth0 -j DROP # запрещаем все остальное

К данной конфигурации так же можно добавить авто-баны при ддосе, но я предпочитаю не использовать их до первой необходимости. Возможно, добавил бы и ограничение на одновременное количество соединений с IP. Все довольно специфично с каждым ресурсом.

Мониторим сервер

Отличным инструментом для мониторинга сервера (нагрузка, сеть, кол-во запросов, логирование исключений) является New Relic. При «правильной» (мой взгляд на это я изложу во второй части) архитектуре даже не будет необходимости мониторить access.log / error.log. Установка и под Win, и под Linux очень проста. Регистрируетесь, добавляете сервер, добавляете репозиторий и ставим демон этого сервиса. А потом в одну строчку его активируем и запускаем. Must have, в общем.

Мониторим файловую систему

Неплохо было следить за изменениями файловой системы, вдруг кто чего залил или изменил? Linux для мониторинга файловой системы предоставляет отличное API — inotify (более подробная статья от youROCK). И существует уже готовый скрипт на perl (конечно, есть аналоги) — iwatch, который полностью автоматизирует процесс мониторинга нужных нам директорий. Установка в debian системах проста:

apt-get install iwatch


И получаем отличные часики Конфиг файл находится по адресу etc/iwatch/iwatch.xml и выглядит следующим образом:

<?xml version="1.0" ?>
<!DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" >

<config lang="xml">
  <guard email="youremail@gmail.com" name="IWatch"/>
  <watchlist>
    <title>Operating System</title>
    <contactpoint email="root@localhost" name="Administrator"/>
    <path type="single" syslog="on">/bin</path>
    <path type="single" syslog="on">/sbin</path>
    <path type="single">/etc</path>
    <path type="recursive">/lib</path>
    <path type="exception">/lib/modules</path>
  </watchlist>
</config>

Все довольно очевидно. Перечисляем нужные нам папки, что надо — исключаем и указываем почту, куда слать отчеты (на gmail все отлично приходит).

Если решите просто скачать скрипт с sourceforge, то нужные зависимости в perl доставляются очень просто — cpan #modulename, например cpan XML::SimpleObject::LibXML.

AppArmor, SELinux, chroot и другие приблуды

К сожалению, если в гугле ввести «SELinux» то первой подсказкой будет «SELinux disable». С AppArmor похожая песня. Частично это связано с тем, что эти средства защиты включены по-умолчанию и доставляют проблемы работающему софту. И ты уже потратил битый час и выпил третью кружку кофе, разбираясь, почему не работает нужный скрипт/софт. Я бы предложил все-таки разобраться с этими «магиями мира Linux» и ознакомиться со следующими статьями:


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

IPS, IDS, WAF

Если у вас уже действующий бизнес-проект, то предлагаю пропустить чтение каких-либо статей по настройке WAF/IPS/IDS и воспользоваться готовыми решениями, например от F5 или от Cloudflare. С Cloudflare вообще куча проблем (для атакующих). Если я вижу, что ресурс на нем, это сразу означает потраченное время х 5. Могу сказать, что Anonymous, 1337day.com (inj3ct0r) и другие pro-security используют именно Cloudflare (никакой рекламы, это просто факт). Конечно, есть варианты обхода и этих систем, но они обычно требуют от атакующего дополнительных растрат, к примеру на аренду машин в облаке с кучей разных IP-адресов, но это уже тема немного другого топика.

Ну а если проект только в начале своего пути и нет денег на подобные сервисы, то предлагаю следующие статьи к прочтению:


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

Безопасность DNS

Если Вы используете свой DNS-сервер (или чей-либо) не забудьте проверить AXFR-запросы к нему (ним) для используемых доменов. Подробно описывал проблему здесь.

Grsecurity

Надеюсь, когда-нибудь мои руки дойдут до Grsecurity. И тогда я добавлю информацию о нем в топик.

Про ведение бэкапов, верные chmod/chown, мониторинг логов (error.log в т.ч.) и событий системы я умолчу, это должно быть само собой разумеющимся.

Серии:
Sergey Belov @BeLove
карма
236,7
рейтинг 13,6
Пользователь
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (47)

  • +8
    Спасибо за how-to!
    Хотелось бы похожую инструкцию, но для rails-окружения и вариант для asp.net окружения под windows server.
    • +2
      Спасибо!
      На самом деле к php в данной статье привязка минимальна, буквально 2 момента с fix_pathinfo и location /index.php.
      Но вторая часть будет посвящена архитектуре сайта именно на php (хотя и там многие моменты переносимы абсолютно на любой язык разработки).
      Так что рекомендации выше можно смело использовать и совершенно на другом окружении.
      С RoR у меня опыт есть, но он не велик. А с ASP еще меньше. Так что я бы еще поднабрался опыта там (сейчас приоритетной RoR). А может, кто-нибудь другой поделится схожими советами для RoR / ASP.Net
      • +4
        >А может, кто-нибудь другой поделится схожими советами для RoR
        скоро будет :)
        • 0
          очень ждём!
        • 0
          Буду ждать :]
    • –7
      К сожалению не могу плюсануть, но очень хочу похожую статью про asp.net/win.
      • 0
        А, случаем, оф. маны по этому поводу не читали на сайте майкрософта?
  • +1
    Спасибо за содержательный рассказ. К связке nginx+php-fpm тоже пришёл экспериментальным путём — очень доволен. Автор, а планируется ли материал по анти-спам защите? В особенности — почты. А то перекопал кучу форумов, перепробовал несколько технических решений, а в итоге заработал понимание того, что я ничё в этом (exim/sendmail/etc) не понимаю, и полную кашу в голове. Думаю, такие вопросы мучают не одного меня :)
    • 0
      Спасибо. По поводу антиспама — на Хабре множество статей на эту тему, при чем очень скомпонованных, с примерами и т.п.
      Думаю, если есть «каша» — то лучше разобраться с ней самому или написать авторам в личку.
  • +5
    Верней будет:
    add_header X-XSS-Protection "1; mode=block";
    

    add_header X-Content-Security-Policy "allow 'self';";
    


    С двоеточием не думаю что запустится
    • 0
      Абсолютно верно, исправил.
  • +5
    А все же не умолчите, чем делаете мониторинг системы и error.log?..
    • 0
      Вообще что-то я упустил еще new relic и некоторые другие моменты, сейчас добавлю.
      Тут зависит от архитектуры приложения. Нужно так заворачивать location, чтобы можно было даже не использовать сторонние анализаторы логов. Постараюсь развернуть этот вопрос во второй части.
      • 0
        да, большое спасибо.
  • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      Расскажите про ситуации.
      Если сравнить количество тех же эксплойтов, вышедших для апача за последний год-два и для nginx'a, то у апача было проблем больше.
      • НЛО прилетело и опубликовало эту надпись здесь
        • +2
          Количество эксплойтов, найденных под Windows, например — большое, но это говорит только о том, что она более известна хакерам.

          Учитывая разницу в распространенности винды и никсов на серверах — это говорит о кривости архитектуры винды и незаинтересованности МС делать защищенную систему. Системы с открытым исходным кодом хакерам известны очевидно намного больше, чем проприетарные. А эксплойтов на порядки меньше.

          ЗЫ: Лично мне интересно какие такие правила для mod_rewrite нельзя настроить под nginx.
          • НЛО прилетело и опубликовало эту надпись здесь
            • +1
              Вы делаете сейчас очень распространенную ошибку. Причем я Вам на нее указал в первой строчке моего предыдущего коммента… ОС, более распространенная где? На клиентских машинах — тут, да, windows более распространена. Но мы вроде не про клиентские. А на серверах — тут юниксы в большинстве. И согласитесь, владельцу ботнета намного выгоднее пополнять ряды ботов из ВДСок и серверов, с соотвствующими мощностями и каналами, чем из клиентских машин. А эксплойтов по-прежнему меньше под никсами.
              • НЛО прилетело и опубликовало эту надпись здесь
                • +1
                  «это банально сложнее, там не работают механизмы заражения, требующие, чтобы пользователь нажал кнопочку «Да, установить»»

                  Так мы про эксплойты или механизмы заражения? Не вырывайте обсуждение из контекста. Сложнее сервера заразить по тому, что в них доступно меньше уязвимостей, потому что сервера под более защищенными ОС, чем винда.

                  " и самих серверов на порядок меньше, чем клиентских машин. Посему и до сих пор куда как эффективнее строить свой ботнет на клиентских машинах."

                  Только вот опять же мощности, а особенно каналы на серверах и клиента различаются на порядки, да и включены они постоянно.

                  "«А эксплойтов по-прежнему меньше под никсами.» — и как Вы их считали?"

                  Оттолкнулся от этих ваших слов.

                  «Количество эксплойтов, найденных под Windows, например — большое, но это говорит только о том, что она более известна хакерам.»
                  • НЛО прилетело и опубликовало эту надпись здесь
          • +1
            Из недавнего поста про датацентры гугла:
            Все эти системы являются закрытыми и кастомизированными — Google объясняет это тем, что закрытые и кастомизированные системы очень устойчивы против внешних атак и в них значительно меньше уязвимостей

            Security through obscurity — это тоже неправильно, но атаковать вслепую сложнее, чем по известным уязвимостям.
  • +4
    Читаю, думаю «надо Белову дать ссылку, понятно что все знает, но тут просто как чек-листом можно пройти — проверить». А потом дохожу до строки автора)
    • +6
      this awkward feeling when people recommend you to read your own articles :)
      • 0
        Соглашусь :3
  • 0
    Апач, конечно, не торт, а вот mod-itk очень даже.
    • 0
      Да, очень спасает, когда много сайтов на одном сервере.
  • 0
    Уважаемый автор, не планируете ли написать нечто подобное, но для связки Апач — Томкат? Уж очень мало русскоязычной информации на эту тему
  • +2
    Спасибо большое за изложенный опыт!
  • 0
    круто, серверная безопасность обычно лежит на плечах админов — надо и самому разобраться
  • 0
    Отличная статья! Радует, что всё больше и больше начало появляться простых и доступных статей по ИБ для конечного пользователя.
  • +1
    Есть такие ресурсы как stumbleupon, surfingbird, которые показывают твой сайт во фрейме, и если заблокировать всё, то будет неочень.
  • 0
    Я сам давно отказался от апача, но использую не nginx, а lighttpd. В принципе, многие описанные здесь вещи можно применить и к нему.
    Пользительное чтиво.
  • +1
    -A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT # ssh
    SSH все же лучше с 22-го порта убрать. Еще вы не упомянули такую чудесную софтину, как fail2ban.
    Замечательный мануал у вас получился.
    • 0
      Опередили.

      Еще бы, если все совсем серьезно, можно использовать port knocking. От себя посоветую делать «открывающий» порт между двумя «закрывающими», например, закрываем — 1499,1501, а закрываем по открываем, тогда даже при полном сканировании портов (от которого можно и отдельно закрываться) ssh злоумышленником обнаружен не будет.
    • 0
      Использование дефолтных портов само по себе не представляет опасности, если уверены, что в системе нет откровенно слабых паролей (или аккаунтов с именами = паролю). Для блокировки назойливых брутеров и чтобы не лицезреть засорённые логи, да, fail2ban вполне эффективен + можно ограничить доступ определёнными подсетями и/или странами (geoip).
      Удобство же дефолтных портов состоит в том, что при подключении не надо явно указывать альтернативный порт, в особенности если часто подключаетесь посредством ручного набора команд.
      • 0
        Вы уверены в 100% невозможности обнаружения 0-day в openssh той версии, которая стоит у Вас на сервере? И зачем нужен частый ручной набор команд?
        • 0
          На моей памяти, конкретно в OpenSSH уже очень давно ничего смертельного не встречается. Да и если кто надумает целиться в конкретные системы, то смена номера порта уже никак не поможет. Случайным же любителями что-нибудь взломать (сюда же можно отнести и любителей массового сканирования интернетов на известные уязвимости) серьёзный 0day обычно недоступен.

          И зачем нужен частый ручной набор команд?

          Когда у вас десятки *nix серверов, то так или иначе нужен. Скрипты или алиасы в шелле что ли создавать для всего?
          • 0
            «Да и если кто надумает целиться в конкретные системы, то смена номера порта уже никак не поможет. „

            А если смена номера порта+защита от портскана? ssh — отличается от того же nginx'a тем, что пашет из-под рута и плохо ограничивается RBAC'ом () Взлом его рутование системы. Соотвественно, представьте себе, что человек держит “веб-ресурс», на котором есть ssh ngninx+php-fpm. nginx не из под рута, очевидно, php-fpm тоже, да еще и в чруте. И более менее доступный способ компрометации сервера остается взлом ssh.

            Может я конечно и параноидальная истеричка, но просто таки неуютно себя чувствую, когда у меня сервисы, запущенный из под рута во весь мир смотрят. Эксгибиоционизм этой какой-то.

            «Скрипты или алиасы в шелле что ли создавать для всего?»

            Да, скрипты или алиасы. Они ж как раз нужны для работы для автоматизации однотипной работы.
            • +1
              пашет из-под рута

              более менее доступный способ компрометации сервера остается взлом ssh

              Вы, похоже, не в теме. Для начала разберитесь, как вообще OpenSSH сервер устроен, privelege separation не просто так придумали. Мне доводилось глубоко ковыряться в исходном коде 3.x и 4.x, никаких опасений данное ПО у меня не вызывает, тем более разрабатывают его те же серьёзные люди, что и OpenBSD. Стоит только заметить, что во всех современных дистрибутивах собственно аутентификация пользователей по умолчанию осуществляется через PAM, если уж даже предполагать наличие уязвимостей на этапе до успешной аутентификации.
              Чего больше стоит опасаться, так это уязвимостей в Exim, используемом как MTA по умолчанию в Debian-based дистрибутивах, тем более что громкие случаи уже были. Менять порты в данном случае не вариант, поскольку нужно принимать внешнюю почту.

              Но вообще, поверьте, «получение рута» — далеко не самоцель адекватных взломщиков в подавляющем большинстве случаев, равно как и повышенный интерес к уязвимостям в сетевых сервисах, работающих без ограничения привилегий.
  • +2
    Во-первых — всем спасибо за отзывы! Вторую часть по разработке постараюсь сделать не менее полезной. И если статья будет выходить большой, тогда может будет и третья часть.
    Во-вторых, обновил топик, добавил информацию про new relic (must have), на который я буду опираться во второй части, а так же добавил хак для nginx'a для защиты от CSRF.
  • 0
    Киньте ссылку по корректной установке прав для веб-разработки
  • +1
    Правила для «location /index.php» понятны, а для остальных файлов?
    Имеет ли право на жизнь конструкция вида
    location / {
    location ~ .*\.(php)?$
    {
    deny all;
    }
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    try_files $uri /index.php$uri;
    }

    ?
    • 0
      Тоже очень интересует этот вопрос. Как запретить запросы ко всем PHP-файлам, кроме index.php? Может кто-то подсказать?

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