notby.NET Logo

Запрет доступа по IP адресу к веб-серверу Nginx без указания домена сайта

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

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

Если ты ленивый или у тебя мало времени, то переходи сразу ко второй части статьи. В ней показан процесс создания серверов по умолчанию и разрешения доступа к веб-серверу nginx только по доменным именам сайтов.

1. Конфигурация nginx без использования серверов по умолчанию (параметр default_server)

1.1. Пример файла конфигурации nginx.conf и сайта

Так выглядит конфигурационный файл nginx.conf без указания блоков server по умолчанию:

# Пример минимальной базовой конфигурации nginx без использования параметра default_server
worker_processes auto;
events {
	worker_connections  1024;
}
http {
	include			mime.types;
	default_type	application/octet-stream;
	access_log		off;

	# Настройка блоков server для каждого сайта в отдельном файле конфигурации.
	include conf/*.conf;
}

В каталоге conf/ находятся конфигурационные файлы для каждого сайта, который обсуживает данный сервер nginx. Блоки server для сайтов могут находится сразу в nginx.conf (основной конфигурационный файл nginx).

Например, файл конфигурации для сайта notby.net выглядит так:

# Блок server для доступа к сайту по HTTP (80 порт)
server {
	listen 80; # Порт сервера.
	server_name notby.net; # Домен сайта.
	
	#Перенаправление всего трафика с http на https
	location / {
		return 301 https://$host$request_uri;
	}
}
# Блок server для доступа к сайту по HTTPS (443 порт)
server {
	listen 443 ssl; # 443 порт, поддержка SSL.
	http2 on; # Разрешить использовать HTTP/2.
	server_name notby.net; # Домен сайта.
	
	# Пути к списку сертификатов и настройка SSL
	ssl_certificate /usr/local/etc/letsencrypt/live/notby.net/fullchain.pem;
	ssl_certificate_key  /usr/local/etc/letsencrypt/live/notby.net/privkey.pem;
	ssl_trusted_certificate /usr/local/etc/letsencrypt/live/notby.net/chain.pem;
	ssl_protocols TLSv1.3; # Разрешенные протоколы (только TLS 1.3).
	ssl_prefer_server_ciphers off; # Использовать клиентские шифры.
	ssl_stapling on; # Обязательна ssl_trusted_certificate директива.
	ssl_stapling_verify on;

	location / {
		root	/usr/local/www/notby.net/; # Каталог сайта.
		index	index.html index.php; # Файлы в качестве индекса.
	}
}

Если у директивы listen есть параметр default_server, то сервер, в котором описана эта директива, будет сервером по умолчанию для указанной пары адрес:порт. Если же директив listen с параметром default_server нет, то сервером по умолчанию будет первый сервер, в котором описана пара адрес:порт.nginx: документация (модуль ngx_http_core_module)

В таком случае, сервером по умолчанию будет первый по алфавиту конфигурационный файл в котором описана переменная listen и для нее указан порт или адрес:порт.

1.2. Проверка доступа к nginx через IP адрес

Допустим, первыми серверами для HTTP и HTTPS будут блоки server для сайта из примера выше.

Через утилиту cURL или через веб-браузер зайдем на свой веб-сервер по IP адресу с использованием HTTP протокола

curl -I http://80.95.110.25
Через утилиты cURL в консоли Debian выведен ответа веб-сервера при доступе к нему через IP-адрес по протоколу HTTP, веб-сервер nginx оправил ответ HTTP/1.1 301 Moved Permanently

Произошло перенаправление на HTTPS. Но, если в настройке сервера для сайта не будет перенаправления, то может открытся веб-сайт по IP адресу.

Теперь зайдем с использованием HTTPS протокола. Добавим параметр v для утилиты cURL, чтобы вывести более детальную информацию и сведения о сертификате

curl -vI https://80.95.110.25
Через утилиты cURL в консоли Debian выведен ответа веб-сервера при доступе к нему через IP-адрес по протоколу HTTPS, веб-сервер nginx оправил ответ с информацией об сертификате, отображено доменное имя notby.net для сертификата.

Выведена информация о сертификате, в частности доменное имя сайта. Значит, что на этом IP находится данный сайт. В плане приватности и безопасности в скрытии этой информации смысла нет, так как через DNS будет и так видно сайты, которые находятся на данном IP адресе. Не совсем правильно выдавать подобную информацию при доступе через IP адрес и это надо исправить.

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

Это является критической уязвимостью приватности и безопасности в обрисованном выше случае. Так как при запросе через IP адрес веб-сервера, будет выдана информация о сертификате, в частности доменное имя сайта и сведения о владельце.

Если зайти через веб-браузер, то будет выведено сообщения о неподходящем сертификате и для какого сайта он действителен

Веб-сайты подтверждают свою подлинность с помощью сертификатов. Firefox не доверяет этому сайту, потому что он использует сертификат, недействительный для 80.95.110.25. Сертификат действителен только для notby.net. Код ошибки: SSL_ERROR_BAD_CERT_DOMAIN

Посмотреть сертификат веб-сервера можно нажав для этого на ссылку Просмотреть сертификат. Если нажать Принять риск и продолжить, то откроется веб-сайт через IP адрес.

2. Конфигурация nginx с указанием серверов по умолчанию (параметр default_server)

В конфигурации nginx блок server становится по умолчанию, если к директиве listen добавлен параметр default_server. Необходимо создать два блока server и сделать их серверами по умолчанию для 80 порта и 443 порта соответственно. В этих блоках нужно ограничить доступ к веб-серверу по IP адресу без указания домена сайта.

Блок сервера по умолчанию для HTTP протокола (80 порт) с запретом доступа и сбросом входящего соединения:

server {
	listen 80 default_server; # Сервер по умолчанию для 80 порта.
	deny all; # Запретить доступ всем.
	return 444; # Закрыть соединение без ответа.
}

Блок сервера по умолчанию для HTTPS протокола (80 порт) с отклонением операций SSL handshake (рукопожатия) и закрытием соединения:

server {
	listen 443 ssl default_server; # Сервер по умолчанию для 443 порта.
	http2 on; # Разрешить использовать HTTP/2.
	ssl_reject_handshake on; # Отклонить рукопожатие.
}

Эти два блока необходимо добавить в файл конфигурации nginx.conf своего веб-сервера.

2.1. Пример файла конфигурации nginx.conf

Так выглядит конфигурационный файл nginx.conf с указанием блоков server по умолчанию для 80 и 443 портов:

# Пример минимальный базовой конфигурации nginx с указанием параметра default_server
# и пояснением переменных внутри блоков server.
worker_processes auto;
events {
	worker_connections  1024;
}

http {
	include			mime.types;
	default_type	application/octet-stream;
	access_log		off;
	
	# Сервер по умолчанию для HTTP (80 порт)
	server {
		# В директиве listen указан только 80 порт и параметр default_server,
		# значит этот блок server по умолчанию для всех сетевых интерфейсов.
		listen 80 default_server;
		# Запретить доступ всем, указан для безопасности.
		deny all;
		# Переменная return отправляет код ответа HTTP.
		# Чтобы закрыть соединение без отправки ответа, использовать 444 код.
		return 444;
		# Вместо 444 можно указать любой код 404 или любой другой.
		#return 404;
	}

	# Сервер по умолчанию для HTTPS (443 порт)
	server {
		# В директиве listen указан только 80 порт и параметры ssl, default_server,
		# значит этот server по умолчанию для всех сетевых интерфейсов.
		listen 443 ssl default_server;
		# Разрешить использовать HTTP/2.
		http2 on;
		# Операции SSL handshake будут отклонены и соединение закрыто.
		ssl_reject_handshake on;
		# Указать глобально для всего сервера версию протокола защиты HTTPS.
		# Если в другом блоке для сайта указать TLSv1.2, она не сработает.
		ssl_protocols TLSv1.3;
	}
	
	# Настройка блоков server для каждого сайта в отдельном файле конфигурации.
	include conf/*.conf;
}

Если в блоке server для HTTPS не указывать директиву ssl_reject_handshake, то при запуске веб-сервера будет выведена ошибка nginx: [emerg] no "ssl_certificate" is defined for the "listen ... ssl" directive in nginx.conf и он не запустится. Директива ssl_reject_handshake была специально добавлена для подобной задачи в версии nginx 1.19.4.

2.2. Проверка безопасности nginx при доступе через IP адрес

Через утилиту cURL или через веб-браузер зайдем на свой веб-сервер по IP адресу с использованием HTTP протокола

curl -I http://80.95.110.25
Через утилиты cURL в консоли Debian выведен ответа веб-сервера при доступе к нему через IP-адрес по протоколу HTTP, веб-сервер nginx оправил ответ HTTP/1.1 404 Not Found

В случае указания кода 444 соединение было закрыто сразу и был получен пустой ответ от сервера. Если использовать код 404, будет выведено стандартное сообщение, что запрашиваемая страница не найдена.

Теперь зайдем с использованием HTTPS протокола. Добавим параметр v для утилиты cURL, чтобы вывести более детальную информацию

curl -vI https://80.95.110.25
Через утилиты cURL в консоли Debian выведен ответа веб-сервера при доступе к нему через IP-адрес по протоколу HTTPS, веб-сервер сбросил рукопожатие и закрыл соединение.

Веб-сервер сбросил соединение и нечего не ответил на рукопожатие клиента. Информация для нарушения приватности и безопасности передана не была, даже не известно, что за веб-сервер.

Если зайти через веб-браузер, то будет выведено сообщение об ошибке защищённого соединения

Ошибка при установлении защищённого соединения. При соединении с 80.95.110.25 произошла ошибка. SSL-узел не имеет сертификата для запрошенного DNS-имени. Код ошибки: SSL_ERROR_UNRECOGNIZED_NAME_ALERT

Веб-браузер не смог соединится и распознал это как ошибку SSL_ERROR_UNRECOGNIZED_NAME_ALERT, никакая информация передана не была.