Выделенный Web-сервер на основе nginx – отличный способ повышения производительности Web-сайтов. В скорости обработки статического контента ему просто нет равных: он легко выдерживает несколько тысяч одновременных соединений и может быть легко оптимизирован и подогнан под любую конфигурацию. Однако? выступая в качестве фронт-энда для Apache, nginx оказывается наиболее уязвимым местом всей Web-инфраструктуры, поэтому безопасности nginx необходимо уделить особое внимание.
Эта статья – своего рода ликбез, или, если хочешь, резюме всех техник повышения безопасности nginx. В ней не будет теории, описания основ настройки Web-сервера и прочей воды. Вместо этого ты получишь исчерпывающий практический материал, описывающий все основные шаги, которые необходимо проделать для того, чтобы получить по-настоящему защищенный Web-сервер.
Установка
Пакет nginx доступен в прекомпилированном виде для любого дистрибутива. Однако собрав сервер самостоятельно, ты сможешь сделать его более компактным и надежным, а также получишь возможность изменить строку приветствия Web-сервера, чтобы отбить несмышленых скрипт-кидди.
Измени строку приветствия Web-сервера
Скачай исходники nginx, открой файл src/http/ngx_http_header_filter_module.c и найди следующие две строки:
static char ngx_http_server_string[] = "Server: nginx" CRLF; static char ngx_http_server_full_string[] = "Server: " NGINX_VER CRLF;
Замени их на что-то вроде этого:
static char ngx_http_server_string[] = "Server: ][ Web Server" CRLF; static char ngx_http_server_full_string[] = "Server: ][ Web Server" CRLF;
Удали все неиспользуемые тобой nginx-модули
Некоторая часть модулей nginx подключается к Web-серверу прямо во время компиляции, и любой из них таит в себе потенциальную опасность. Возможно, в будущем в одном из них будет найдена уязвимость, и твой сервер окажется под угрозой. Отключив ненужные модули, ты сможешь значительно снизить риск возникновения такой ситуации.
Выполни сборку с помощью следующих команд:
# ./configure --without-http_autoindex_module --without-http_ssi_module # make
# make install
Так ты получишь nginx с заранее отключенными (и в большинстве случаев бесполезными) модулями SSI (Server Side Includes) и Autoindex. Чтобы узнать, какие модули можно безболезненно выбросить из Web-сервера, запусти скрипт configure с флагом ‘—help’.
Препарируем nginx.conf
После установки nginx следует настроить. На страницах журнала уже был материал, описывающий этот процесс, мы же будем придерживаться темы статьи и поговорим о способах повышения безопасности сервера.
Отключи показ версии сервера на всех ошибочных страницах
Добавь в файл nginx.conf строку “server_tokens off”. Это заставит nginx скрывать информацию о типе и версии Web-сервера на страницах, генерируемых в ответ на ошибочный запрос клиента.
Настрой защиту от срыва стека
Добавь в секцию server следующие строки:
# vi /etc/nginx/nginx.conf
# Максимальный размер буфера для хранения тела запроса клиента client_body_buffer_size 1K; # Максимальный размер буфера для хранения заголовков запроса клиента client_header_buffer_size 1k; # Максимальный размер тела запроса клиента, прописанный в поле Content-Length заголовка. Если сервер должен поддерживать загрузку файлов, это значение необходимо увеличить client_max_body_size 1k; # Количество и размер буферов для чтения большого заголовка запроса клиента large_client_header_buffers 2 1k;
Обрати внимание на директиву large_client_header_buffers. По умолчанию, для хранения строки URI nginx выделяет четыре буфера, размер каждого из которых равен размеру страницы памяти (для x86 это 4 Кб). Буферы освобождаются каждый раз, когда по окончанию обработки запроса соединение переходит в состояние keep-alive. Два буфера по 1 Кб могут хранить URI длиной только 2 Кб, что позволяет бороться с ботами и DoS-атаками.
Для повышения производительности добавь следующие строки:
# vi /etc/nginx/nginx.conf
# Таймаут при чтении тела запроса клиента client_body_timeout 10; # Таймаут при чтении заголовка запроса клиента client_header_timeout 10; # Таймаут, по истечению которого keep-alive соединение с клиентом не будет закрыто со стороны сервера keepalive_timeout 5 5; # Таймаут при передаче ответа клиенту send_timeout 10;
Контролируй количество одновременных соединений
Для защиты Web-сервера от перегрузки и попыток осуществить DoS-атаку добавь в конфиг следующие строки:
# vi /etc/nginx/nginx.conf
# Описываем зону (slimits), в которой будут храниться состояния сессий. Зона размером 1 Мб может хранить около 32000 состояний, мы устанавливаем ее размер равным 5 Мб limit_zone slimits $binary_remote_addr 5m; # Задаем максимальное количество одновременных соединений для одной сессии. По сути, это число задает максимальное количество соединений с одного IP limit_conn slimits 5;
Первая директива должна находиться в секции HTTP, вторая – в секции location. Когда количество соединений выйдет за пределы лимитов, клиент получит сообщение «Service unavailable» с кодом 503.
Разреши коннекты только к своему домену
Взломщики могут использовать ботов для сканирования подсетей и поиска уязвимых Web-серверов. Обычно боты просто перебирают диапазоны IP-адресов в поисках открытых 80 портов и посылают запрос HEAD для получения информации о веб-сервере (или главной странице). Ты можешь легко предотвратить такой скан, запретив обращение к серверу по IP-адресу (добавить в подсекцию location):
# vi /etc/nginx/nginx.conf
if ($host !~ ^(host.com|www.host.com)$ ) { return 444; }
Ограничь количество доступных методов обращения к Web-серверу
Некоторые боты используют различные методы обращения к серверу для попытки определения его типа и/или проникновения, однако в документе RFC 2616 четко сказано, что Web-сервер не обязан реализовывать их все, и неподдерживаемые методы могут просто не обрабатываться. Сегодня используемыми остаются только методы GET (запрос документа), HEAD (запрос заголовков сервера) и POST (запрос на публикацию документа), поэтому все остальные можно безболезненно отключить с помощью помещения следующих строк в секцию server конфигурационного файла:
# vi /etc/nginx/nginx.conf
if ($request_method !~ ^(GET|HEAD|POST)$ ) { return 444; }
Отшивай ботов
Другой способ блокирования ботов, сканеров и прочей нечисти основан на определении типа клиента (user-agent). Он не слишком эффективен, потому как большинство ботов косят под вполне легитимные браузеры, но в ряде случаев остается полезным:
# vi /etc/nginx/nginx.conf
# Блокируем менеджеры загрузки if ($http_user_agent ~* LWP::Simple|BBBike|wget) { return 403; } # Блокируем некоторые типы ботов if ($http_user_agent ~* msnbot|scrapbot) { return 403; }
Блокируй Referrer-спам
Если твой сайт публикует Web-логи в общедоступном виде, ты легко можешь стать жертвой Referrer-спама (когда спам-боты обращаются к твоему серверу, указывая в заголовке referrer – адрес рекламируемого сайта). Такой вид спама может легко испортить SEO-рейтинги интернет-страницы, поэтому его необходимо блокировать в обязательном порядке. Один из способов сделать это – занести наиболее частые слова, встречающиеся в адресах рекламируемых сайтов, в черный список.
# vi /etc/nginx/nginx.conf
# Секция server if ( $http_referer ~* (babes|forsale|girl|jewelry|love|nudit|organic|poker|porn|sex|teen) ) { return 403; }
Блокируй хотлинк
Хотлинк – это включение в страницу изображения (или иного контента) с другого сайта. По сути, это воровство, потому как изображение, на которое ты потратил не один час своего свободного времени, не только свободно используется другими, но и создает нагрузку на твой Web-сервер, не приводя на него посетителей. Для борьбы с хотлинками достаточно сделать так, чтобы изображения отдавались клиенту только в том случае, если он запросил их, уже находясь на сайте (другими словами, заголовок referrer-запроса должен содержать имя твоего сайта). Добавь в секцию server конфигурационного файла nginx.conf следующие строки (host.com – это адрес твоего сайта):
# vi /etc/nginx/nginx.conf
location /images/ { valid_referers none blocked www.host.com host.com; if ($invalid_referer) { return 403; } }
В качестве альтернативы ты можешь настроить сервер на отдачу специального баннера с сообщением о воровстве вместо запрашиваемого изображения. Для этого замени строку «return 403» на строку:
rewrite ^/images/uploads.*\.(gif|jpg|jpeg|png)$ http://www.host.com/banned.jpg last
Защищай важные каталоги от посторонних
Как и любой другой Web-сервер, nginx позволяет регулировать доступ к каталогам на основе IP-адресов и паролей. Эту возможность можно использовать для закрытия некоторых частей сайта от посторонних глаз. Например, для отрезания URI от внешнего мира:
# vi /etc/nginx/nginx.conf
location /uploads/ { # Разрешаем доступ только машинам локальной сети allow 192.168.1.0/24; # Отшиваем всех остальных deny all; }
Теперь к документам каталога uploads будут иметь доступ только пользователи локальной сети. Для установки пароля придется проделать более сложные действия. Сначала необходимо создать приватный для nginx-файл паролей и добавить в него необходимых пользователей (в качестве примера добавим пользователя admin):
# mkdir /etc/nginx/.htpasswd # htpasswd -c /etc/nginx/.htpasswd/passwd admin
Далее открой файл nginx.conf и впиши в него следующие строки:
# vi /etc/nginx/nginx.conf
location /admin/ { auth_basic "Restricted"; auth_basic_user_file /etc/nginx/.htpasswd/passwd; }
Новых пользователей можно добавить с помощью следующей команды:
# htpasswd -s /etc/nginx/.htpasswd/passwd пользователь
Используй SSL
Если твой сайт работает с приватными данными пользователей, такими как номера кредитных карт, пароли от других сервисов, или же предоставляет доступ к другой важной информации, которая может стать лакомым кусочком для третьих лиц, позаботься о шифровании. Nginx хорошо работает с SSL, и этой возможностью нельзя пренебрегать.
Для настройки SSL-шифрования средствами nginx достаточно выполнить несколько простых шагов. Сначала ты должен создать сертификат с помощью следующей последовательности команд:
# cd /etc/nginx # openssl genrsa -des3 -out server.key 1024 # openssl req -new -key server.key -out server.csr # cp server.key server.key.org # openssl rsa -in server.key.org -out server.key # openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
Затем описать сертификат в конфигурационном файле nginx:
# vi /etc/nginx/nginx.conf
server { server_name host.com; listen 443; ssl on; ssl_certificate /etc/nginx/server.crt; ssl_certificate_key /etc/nginx/server.key; access_log /etc/nginx/logs/ssl.access.log; error_log /etc/nginx/logs/ssl.error.log; }
После этого можно перезагрузить Web-сервер:
# /etc/init.d/nginx reload
Естественно, без поддержки со стороны самого Web-сайта это делать бессмысленно.
Другие способы
Установи правильные значения системных переменных
Открой файл /etc/sysctl.conf и помести в него следующие строки:
# vi /etc/sysctl.conf
# Защита от smurf-атак net.ipv4.icmp_echo_ignore_broadcasts = 1 # Защита от неправильных ICMP-сообщений net.ipv4.icmp_ignore_bogus_error_responses = 1 # Защита от SYN-флуда net.ipv4.tcp_syncookies = 1 # Запрещаем маршрутизацию от источника net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.default.accept_source_route = 0 # Защита от спуфинга net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 # Мы не маршрутизатор net.ipv4.ip_forward = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.default.send_redirects = 0 # Включаем ExecShield kernel.exec-shield = 1 kernel.randomize_va_space = 1 # Расширяем диапазон доступных портов net.ipv4.ip_local_port_range = 2000 65000 # Увеличиваем максимальный размер TCP-буферов net.ipv4.tcp_rmem = 4096 87380 8388608 net.ipv4.tcp_wmem = 4096 87380 8388608 net.core.rmem_max = 8388608 net.core.wmem_max = 8388608 net.core.netdev_max_backlog = 5000 net.ipv4.tcp_window_scaling = 1
Размести корневой каталог Web-сервера на выделенном разделе
Поместив корневой каталог Web-сервера в выделенный раздел и запретив на нем размещение любых исполняемых файлов и файлов-устройств, ты обезопасишь остальную часть системы от тех, кто сможет получить доступ к корню Web-сервера. При этом запись в файле /etc/fstab должна иметь примерно такой вид:
/dev/sda5 /nginx ext4 defaults,nosuid,noexec,nodev 1 2
Помести nginx в chroot/jail-окружение
Любая современная *nix-система позволяет запереть приложение в изолированной среде исполнения. В Linux для этого можно использовать технологии KVM, Xen, OpenVZ и VServer, во FreeBSD – Jail, в Solaris – Zones. Если ни одна из этих технологий не доступна, ты можешь поместить nginx в классический chroot, который хоть и намного более хрупок, но большинство взломщиков остановить сможет.
Установи правила SELinux для защиты nginx
Хорошей альтернативой изолированным средам исполнения являются локальные системы обнаружения и предотвращения вторжений, такие как SELinux или AppArmor. Будучи правильно настроенными, они смогут предотвратить попытки взлома Web-сервера. По дефолту ни одна из них не настроена для работы в связке с nginx, однако в рамках проекта SELinuxNginx (http://sf.net/projects/selinuxnginx/) были созданы правила для SELinux, которые может использовать любой желающий. Остается только скачать и установить:
# tar -zxvf se-ngix_1_0_10.tar.gz # cd se-ngix_1_0_10/nginx # make # /usr/sbin/semodule -i nginx.pp
Настрой брандмауэр
Обычно nginx устанавливают на выделенных машинах, готовых к высокой нагрузке, поэтому зачастую он остается единственным сетевым сервисом, работающим на сервере. Чтобы обезопасить сервер, достаточно создать совсем небольшой набор правил, которые будут открывать 80, 110 и 143-й порты (если, конечно, nginx должен работать еще и как IMAP/POP3-прокси) и закрывать от внешнего мира все остальное.
Ограничь количество соединений с помощью брандмауэра
Для не слишком нагруженного Web-сайта хорошей идеей будет ограничить количество попыток соединений с одного IP-адреса в минуту. Это сможет уберечь тебя от некоторых типов DoS-атак и брутфорса. В Linux это можно сделать с помощью стандартного iptables/netfilter-модуля state:
# iptables -A INPUT -p tcp --dport 80 -i eth0 \ -m state --state NEW -m recent --set # iptables -A INPUT -p tcp --dport 80 -i eth0 \ -m state --state NEW -m recent --update \ --seconds 60 --hitcount 15 -j DROP
Правила урезают лимит на количество подключений с одного IP в минуту до 15. То же можно сделать и с помощью pf:
# vi /etc/pf.conf
webserver_ip="1.1.1.1" table <abuse> persist block in quick from <abuse> pass in on $ext_if proto tcp to $webserver_ip \ port www flags S/SA keep state \ (max-src-conn 100, max-src-conn-rate 15/60, \ overload <abusive_ips> flush)
Кроме лимита на количество последовательных подключений (15 в минуту), данное правило устанавливает дополнительный лимит на количество одновременных подключений равный 100.
Настрой PHP
Если ты используешь nginx в связке с PHP, не забудь настроить и его. Вот как должен выглядеть конфигурационный файл /etc/php/php.ini защищенного сервера:
# vi /etc/php/php.ini
# Отключаем опасные функции disable_functions = phpinfo, system, mail, exec # Максимальное время исполнения скрипта max_execution_time = 30 # Максимальное время, которое может потратить скрипт на обработку данных запроса max_input_time = 60 # Максимальное количество памяти, выделяемое каждому скрипту memory_limit = 8M # Максимальный размер данных, отсылаемых скрипту с помощью метода POST post_max_size = 8M # Максимальный размер загружаемых файлов upload_max_filesize = 2M # Не показывать ошибки PHP-скриптов пользователям display_errors = Off # Включаем Safe Mode safe_mode = On # Включаем SQL Safe Mode sql.safe_mode = On # Позволяем выполнять внешние команды только в этом каталоге safe_mode_exec_dir = /путь/к/защищенному/каталогу # Защищаемся от утечки информации о PHP expose_php = Off # Ведем логи log_errors = On # Запрещаем открытие удаленных файлов allow_url_fopen = Off
Выводы
Применив описанные в статье рекомендации, ты получишь гораздо более защищенный Web-сервер. Но имей в виду, что не все техники подойдут к твоей конфигурации. Например, защита от брутфорса, основанная на урезании размеров буферов, выделяемых nginx под обработку запросов клиентов, может привести к падению производительности, а в некоторых случаях и к сбоям в обработке запросов. Ограничение на количество подключений нанесет сильный удар по производительности даже средненагруженного Web-сайта, но принесет пользу, если страница имеет низкий поток посетителей. Всегда проверяй, как внесенные тобой изменения повлияли на производительность и общую работоспособность Web-страницы.
О герое дня
Nginx – один самых производительных и популярных Web-серверов в мире. Согласно данным Netcraft, он используется для поддержки более чем 12 миллионов Web-сайтов по всему миру, включая таких мастодонтов как Rambler, Yandex, Begun, WordPress.com, Wrike, SourceForge.net, vkontakte.ru, megashara.com, Либрусек и Taba.ru. Грамотная архитектура, основанная на мультиплексировании соединений с помощью системных вызовов select, epoll (Linux), kqueue (FreeBSD) и механизме управления памятью на основе пулов (небольших буферов от 1 до 16 Кб), позволяет nginx не проседать даже под очень высокими нагрузками, выдерживая свыше 10000 одновременных соединений (так называемая проблема C10K). Изначально написан Игорем Сысоевым для компании Rambler и открыт в 2004 году под BSD-подобной лицензией.