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

Я уже довольно долгое время хочу формализовать все свои мысли, опыт, ежедневно применяемый на практике, и многое другое в одном месте и предоставить их общественности. Уверен, многим этот материал будет полезен. Он посвящен различным моментам в конфигурации серверного ПО 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;
}

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

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 # запрещаем все остальное

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

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

Неплохо было следить за изменениями файловой системы, вдруг кто чего залил или изменил? 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 и другие pro-security люди используют именно Cloudflare (никакой рекламы, это просто факт). Конечно, есть варианты обхода и этих систем, но они обычно требуют от атакующего дополнительных растрат, к примеру на аренду машин в облаке с кучей разных IP-адресов, но это уже тема немного другого топика.

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

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

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

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

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

Спасибо автору на хабре.