Продвинутая защита Nginx
Nginx — пока еще один из самых популярных веб-серверов, и ключевой компонент современной корпоративной архитектуры. В этой статье мы рассмотрим базовые параметры конфигурации (доступные "из коробки"), которые упрощают мониторинг, улучшают производительность и усиливают безопасность — в конечном итоге повышая устойчивость вашей инфраструктуры.
Логирование в формате JSON
JSON — лучший выбор формата для логов Nginx по двум основным причинам. Во-первых, он гораздо более читаем для человека. Во-вторых, передача логов в такие системы, как OpenSearch, для последующего мониторинга или в рамках SIEM-решений становится сильно проще.
Вот простой пример из nginx.conf:
log_format json-logger escape=json '{
"type": "access-log",
"time": "$time_iso8601",
"remote-ip": "$remote_addr",
"x-forward-for": "$proxy_add_x_forwarded_for",
"request-id": "$request_id",
"request-length": "$request_length",
"response-bytes": "$bytes_sent",
"response-body-size": "$body_bytes_sent",
"status": "$status",
"vhost": "$host",
"protocol": "$server_protocol",
"path": "$uri",
"query": "$args",
"duration": "$request_time",
"backend-duration": "$upstream_response_time",
"backend-status": "$upstream_status",
"method": "$request_method",
"referer": "$http_referer",
"user-agent": "$http_user_agent",
"active-connections": "$connections_active"
}';
access_log /var/log/nginx/access.log json-logger;
Это приведёт к следующему выводу в файле access.log:
{
"type": "access-log",
"time": "2025-02-25T16:02:54+00:00",
"remote-ip": "130.61.78.239",
"x-forward-for": "130.61.78.239",
"request-id": "38750f2a1a51b196fa0a76025b0d1be9",
"request-length": "258",
"response-bytes": "353",
"response-body-size": "167",
"status": "404",
"vhost": "3.69.78.187",
"protocol": "HTTP/1.1",
"path": "/lib/phpunit/Util/PHP/eval-stdin.php",
"query": "",
"duration": "0.016",
"backend-duration": "0.016",
"backend-status": "404",
"method": "GET",
"referer": "",
"user-agent": "Custom-AsyncHttpClient",
"active-connections": "1"
}
Параметризация запросов
Большие размеры тела запроса, длительные тайм-ауты и чрезмерно увеличенные настройки KeepAlive могут негативно повлиять на производительность. Чтобы повысить эффективность, лучше устанавливать эти параметры на минимально возможные значения — при этом, само собой, соблюдая требования вашего приложения.
Пример из nginx.conf:
client_max_body_size 10M;
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 5s 5s;
Описание параметров:
client_max_body_size
Определяет максимальный размер тела HTTP-запроса, который клиент может отправить. Если лимит превышен, Nginx возвращает ошибку 413 Request Entity Too Large.
client_body_timeout
Задает максимальное время ожидания полного тела запроса. Если за это время тело не получено, соединение будет закрыто.
client_header_timeout
Устанавливает максимальное время ожидания полного заголовка HTTP-запроса от клиента. Если лимит превышен — соединение также закрывается.
keepalive_timeout
Определяет, как долго будет оставаться открытым соединение Keep-Alive после последнего запроса.
Первый параметр (например, 5s) задаёт тайм-аут на стороне сервера. Второй (опциональный) параметр передаётся клиенту как предложение о том, как долго он может держать соединение открытым.
Ограничение количества запросов
На случай если клиент пытается перегрузить веб-сервер частыми запросами, Nginx предоставляет возможность настроить "зоны ограничения запросов" (limit request zones) — для контроля трафика по различным параметрам.
Пример настройки (реверс-прокси с зоной ограничения запросов):
limit_req_zone $binary_remote_addr zone=limitreqsbyaddr:20m rate=15r/s;
limit_req_status 429;
upstream app.localhost {
server localhost:8080;
}
server {
listen 443 ssl;
server_name app.devlab.intern;
location / {
limit_req zone=limitreqsbyaddr burst=10;
proxy_pass http://app.localhost;
}
}
Пояснение параметров:
$binary_remote_addr
Используется для настройки зоны ограничения по IP-адресу клиента. Адрес сохраняется в бинарной форме (это снижает потребление памяти).
zone=limitreqsbyaddr:20m
Создаёт общую (shared) область памяти объёмом 20 МБ с именем limitreqsbyaddr. В этой зоне хранятся данные о лимитах для разных IP-адресов.
rate=15r/s
Ограничивает количество запросов до 15 запросов в секунду на IP. Превышение лимита приводит к отклонению избыточных запросов.
limit_req_status 429;
При превышении лимита Nginx возвращает статус 429 Too Many Requests, что означает: "Слишком много запросов за короткое время".
Эта конфигурация помогает защитить сервисы от перегрузки и злоупотреблений.
Ограничение только на необходимые HTTP-методы
На мой взгляд, ограничение допустимых HTTP-методов только теми, которые действительно необходимы или поддерживаются приложением (например, REST API), — это чистый и логичный способ синхронизации настроек веб-сервера с логикой приложения. Это помогает не только предотвратить неправильное использование API или вызов нежелательных методов, но и блокирует потенциально опасные запросы вроде TRACE. Кроме того, это снижает ненужную нагрузку на сервер, устраняя неподдерживаемые или неуместные запросы.
Пример: разрешён только метод GET (HEAD разрешается по умолчанию):
# HEAD is implicit
limit_except GET {
deny all;
}
Пример: разрешить все методы, кроме TRACE и PATCH:
if ($request_method ~ ^(PATCH|TRACE)$) {
return 405;
}
Простая защита от ботов
Если на сервер поступают запросы от ботов или плохо сконфигурированных сканеров (часто с «говорящими» user-agent'ами), можно внести путаницу и затруднить их работу, возвращая нестандартный статус HTTP от самого Nginx.
Для этого создаём файл bot.protection.conf в директории /etc/nginx/snippets со следующим содержимым:
map $http_user_agent $blacklist_user_agents {
~*wpscan 1;
~*dirbuster 1;
~*gobuster 1;
}
Вы можете дополнять список по мере необходимости.
Внутри конфигурации виртуального хоста (VHost) подключите файл следующим образом:
include /etc/nginx/snippets/bot.protection.conf;
if ($blacklist_user_agents) {
return 444;
}
HTTP 444 также можно «весело» использовать для предотвращения угадывания служебных файлов, таких как .env:
# <your-domain>/.bash_history for example ends with HTTP 444.
location ~ /\. {
return 444;
}
Что означает HTTP 444?
HTTP 444 — это нестандартизированный статус-код, используемый в NGINX, который заставляет сервер мгновенно закрыть соединение без отправки заголовков ответа клиенту. Чаще всего используется для отклонения вредоносных или некорректно оформленных запросов. Интересный побочный эффект: некоторые сканеры не умеют корректно обрабатывать такие ответы, что добавляет дополнительный уровень защиты.
Включение TCP Fast Open
TCP Fast Open — это важное улучшение в Nginx, которое позволяет более эффективно устанавливать TCP-соединения. Эта функция даёт возможность начать передачу данных уже во время начального рукопожатия, что заметно ускоряет процесс установления соединения. Особенно полезна она в условиях высокой сетевой задержки, так как помогает сократить латентность и повысить производительность.
Проверка поддержки TCP Fast Open в ядре Linux
Выполните следующую команду:
cat /proc/sys/net/ipv4/tcp_fastopen
Если в ответе вернётся 1, функция уже включена. Если нет — включите её командой:
echo 1 > /proc/sys/net/ipv4/tcp_fastopen
Использование в конфигурации Nginx
Добавьте параметр fastopen в директивы listen:
listen [::]:443 ssl http2 fastopen=500;
listen 443 ssl http2 fastopen=500;
Число 500 — это количество подключений, которые могут использовать TCP Fast Open одновременно (значение можно адаптировать под вашу нагрузку).
Сжатие с помощью GZip
GZip — это метод сжатия данных, позволяющий уменьшить размер файлов. При этом исходные данные можно полностью восстановить путём распаковки («разархивирования») сжатого файла.
Для веб-приложений и сайтов GZip особенно важен, поскольку HTTP-протокол поддерживает передачу данных в сжатом виде до того, как они попадут в сеть.
Почему GZip важен:
Снижение трафика: при включённом GZip размер передаваемых файлов уменьшается, что приводит к меньшему потреблению пропускной способности.
Экономия на хостинге: меньше трафика — ниже затраты на обслуживание.
Ускорение загрузки для пользователей: посетители получают более лёгкие файлы, что сокращает время загрузки страниц.
Пример конфигурации:
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
Эта настройка включает GZip и указывает типы содержимого, которые следует сжимать (текст, CSS, JavaScript, XML, JSON и др.).