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

в 8:58, , рубрики: nginx, php, security, информационная безопасность, метки: , ,

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

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

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

Автор: BeLove

Источник

Поделиться

* - обязательные к заполнению поля