РУКОВОДСТВО ПО БЕЗОПАСНОСТИ СЕРВЕРОВ

Открытые базы данных на VPS: как закрыть MySQL, PostgreSQL, Redis и MongoDB

Базы данных создавались так, чтобы доверять клиентам. Они исходят из того, что любой, кто достучался до порта, имеет на это право, поэтому база, доступная из интернета, такая лёгкая цель. Документация самого Redis говорит, что он рассчитан на доступ доверенных клиентов внутри доверенных сред, что выставлять его напрямую в интернет обычно плохая идея и что одной команды FLUSHALL внешнему атакующему достаточно, чтобы удалить весь набор данных. Масштаб проблемы не гипотетический: когда Wiz раскрыла в октябре 2025 года уязвимость Redis под названием RediShell, Help Net Security сообщил, что Wiz нашла около 330 000 открытых в интернет экземпляров Redis, примерно 60 000 из них без настроенной аутентификации. Это руководство показывает, как базы данных становятся открытыми на небольшом VPS, как проверить свой сервер за несколько минут и какие именно настройки оставляют MySQL, PostgreSQL, Redis и MongoDB приватными.

Быстрый результат (10 минут)

  • Выведите порты баз данных и адреса привязки: sudo ss -tulpn | grep -E ":(3306|5432|6379|27017)". 127.0.0.1 значит приватно; 0.0.0.0 или [::] значит все интерфейсы.
  • С другой машины проверьте доступность: nc -vz YOUR_SERVER_IP 6379. Успешное подключение значит, что порт открыт для вас.
  • Если используете Docker, посмотрите опубликованные порты: docker ps --format "{{.Names}} {{.Ports}}" и ищите привязки к 0.0.0.0.
  • Проверьте состояние файрвола: sudo ufw status verbose, затем прочитайте раздел о Docker ниже, потому что опубликованные порты контейнеров могут его обходить.
  • Отметьте все базы, у которых не задан пароль или пользователь. Исправьте это раньше всего остального.

Как базы данных оказываются в интернете

Есть четыре распространённых пути, и ни один из них не требует ошибки, которая в тот момент выглядела бы ошибкой.

  • Значение по умолчанию слушает широко. Согласно справочному руководству MySQL, bind_address по умолчанию равен *, то есть сервер принимает TCP/IP-подключения на всех интерфейсах IPv4 и, где это поддерживается, на всех интерфейсах IPv6. Redis, по его документации по безопасности, в конфигурации по умолчанию привязывается ко всем интерфейсам и спасается от беды только защищённым режимом, который можно отключить.
  • Кто-то открыл порт «для теста». PostgreSQL по умолчанию слушает только loopback-адрес, как отмечено в документации по pg_hba.conf, поэтому открытость там обычно означает, что кто-то изменил listen_addresses и добавил разрешающую строку в pg_hba.conf, чтобы подключаться с другой машины.
  • Docker публикует на всё. В документации Docker сказано, что при отображении порта без адреса хоста демон Docker публикует порты на всех адресах хоста, а публикацию по умолчанию там называют небезопасной. Запись -p 5432:5432 в compose-файле выставляет базу на все интерфейсы, а правила Docker могут обходить ufw, как разобрано в руководстве по защите хоста Docker.
  • Аутентификацию так и не включили. Wiz отметила, что в официальных контейнерных образах Redis аутентификация по умолчанию отключена, из-за чего любая открытость превращается в полный доступ.

Проверьте, что открыто прямо сейчас

Начните с того, что сообщает сам сервер, затем подтвердите снаружи, потому что эти два результата могут расходиться, если задействованы Docker или файрвол провайдера.

sudo ss -tulpn | grep -E ":(3306|5432|6379|27017|9200)"

В выводе всё говорит колонка локального адреса. 127.0.0.1:5432 доступен только с самой машины. 0.0.0.0:6379 или [::]:6379 доступен отовсюду, откуда пропускает файрвол. Затем проверьте со второй машины или с ноутбука в другой сети командой nc -vz YOUR_SERVER_IP 5432. Если подключение проходит, порт открыт на практике, что бы ни говорил конфигурационный файл. Наш бесплатный read-only аудит запускает такую же проверку по портам баз данных и сервисов, ничего не меняя.

Всё, что отвечает, считайте уже просканированным. Интернет-сканеры быстро находят новые открытые порты баз данных, поэтому если порт был открыт, исходите из того, что на него уже смотрели.

MySQL и PostgreSQL

MySQL. Привяжите его к loopback-адресу, если удалённому клиенту это действительно не нужно. В секции [mysqld] вашей конфигурации (часто mysqld.cnf) задайте:

[mysqld]
bind-address = 127.0.0.1

Перезапустите службу и снова выполните проверку ss. Затем заведите для каждого приложения отдельную учётную запись, ограниченную хостом, с которого оно подключается, вместо подстановочного хоста. Например, CREATE USER 'app'@'10.0.0.5' IDENTIFIED BY 'a-long-random-password'; и затем GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app'@'10.0.0.5'; делает учётную запись бесполезной с любого другого адреса и не выдаёт глобальных привилегий.

PostgreSQL. Оставьте listen_addresses = 'localhost', пока удалённый доступ не нужен. Когда он нужен, документация PostgreSQL объясняет, что pg_hba.conf определяет, каким клиентам можно подключаться, что используется первая подходящая запись по типу подключения, адресу клиента, базе и имени пользователя и что «проваливания» дальше по списку нет. Пишите узкие записи и никогда не используйте trust: документация описывает его как разрешение любому, кто может подключиться к серверу, войти под любым пользователем PostgreSQL без пароля и какой-либо другой аутентификации.

hostssl  appdb  app  10.0.0.5/32  scram-sha-256

Файл читается при старте и при перезагрузке, поэтому применяйте изменение командой sudo systemctl reload postgresql и проверяйте с разрешённого хоста.

Не уверены, какие порты открыты на вашем сервере?

Бесплатная проверка в режиме только чтения покажет слушающие порты и открытость баз данных без каких-либо изменений.

Получить бесплатную проверку сервера Связаться с инженером по безопасности

Redis и MongoDB

Redis. Совет документации для одиночной машины, выставленной в интернет: закрыть порт Redis файрволом и дополнительно привязать Redis к loopback-интерфейсу строкой bind 127.0.0.1. Добавьте аутентификацию как второй слой: начиная с Redis 6 рекомендуемый способ это списки контроля доступа (ACL) с именованными пользователями и тонкими правами, а прежняя настройка requirepass тоже работает. В документации аутентификация описана как слой избыточности на случай, если файрвол или другая система защиты от внешних атакующих отказали. Запускайте Redis от непривилегированного пользователя redis и ограничивайте через ACL мощные команды вроде CONFIG, которая, как отмечено в документации, позволяет записывать файлы по произвольным путям.

RediShell (CVE-2025-49844) хорошо напоминает, что одной аутентификации недостаточно. Это use-after-free в движке Lua-скриптов Redis, до которого может добраться аутентифицированный атакующий; затронуты версии Redis до 8.2.1 включительно, исправления вышли в релизах вроде 8.2.2, 8.0.4, 7.4.6 и 7.2.11. Если обновиться немедленно нельзя, Help Net Security отмечает, что ограничение команд EVAL и EVALSHA через ACL убирает вектор. Обновляйтесь, включайте аутентификацию и держите порт приватным.

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

net:
  bindIp: 127.0.0.1
security:
  authorization: enabled

Как безопасно работать с закрытой базой

Приватность базы не мешает с ней работать. Для редкого администрирования пробрасывайте порт через SSH вместо того, чтобы открывать его:

ssh -N -L 5432:127.0.0.1:5432 deploy@your-server

Ваш клиент тогда подключается к localhost:5432 на вашей машине, а трафик идёт внутри зашифрованной SSH-сессии. Поскольку используется ваш обычный вход только по ключу, вы заодно получаете защиту, описанную в руководстве по усилению защиты SSH. Для постоянной связи между серверами используйте приватную сеть или VPN вроде WireGuard и привязывайте базу к этому приватному адресу.

Если вам действительно нужно принимать удалённое подключение на публичном адресе, разрешите ровно один источник и ничего больше, например sudo ufw allow from 203.0.113.10 to any port 5432 proto tcp, и требуйте TLS. Чек-лист MongoDB рекомендует TLS для всех соединений, а записи hostssl в PostgreSQL принудительно его включают. Для Docker публикуйте порт только на loopback: -p 127.0.0.1:5432:5432.

Добавьте слои защиты

Приватный адрес привязки это первый слой, а не единственный. Поставьте перед базой firewall с политикой default deny (см. руководство по файрволу в Linux), выдавайте каждой учётной записи приложения только нужные привилегии в духе минимальных привилегий и обновляйте сам движок базы в рамках обычного управления обновлениями.

Планируйте и тот день, когда защита не сработает. Открытая база это классический путь к краже данных и к атакам с удалением и выкупом, поэтому руководство по защите от ransomware и руководство по стратегии резервного копирования относятся к тому же плану. Наконец, оповещайте об изменениях: новый слушающий порт или база, которая вдруг принимает подключения с нового адреса, заслуживает уведомления, а руководство по мониторингу показывает, за чем следить.

Часто задаваемые вопросы

Должны ли MySQL или PostgreSQL когда-либо слушать публичный IP-адрес?

Почти никогда. Если приложению на другом сервере нужен доступ, используйте приватную сеть, VPN или SSH-туннель, разрешите в файрволе только этот единственный адрес источника и требуйте TLS. Публичной открытости, где защитой служит только пароль, следует избегать.

Достаточно ли надёжного пароля, чтобы открыть базу данных в интернет?

Нет. Пароль это один слой, а в документации Redis аутентификация сама описана как избыточность на случай отказа сетевой защиты. До непропатченных уязвимостей, таких как RediShell в Redis, может добраться и аутентифицированный пользователь, поэтому порт должен оставаться приватным независимо от пароля.

Как понять, доступна ли моя база данных из интернета?

Выполните на сервере sudo ss -tulpn и посмотрите локальный адрес каждого порта базы, затем проверьте с другой машины командой nc -vz YOUR_SERVER_IP PORT. Адрес привязки 127.0.0.1 приватный, а 0.0.0.0 или [::] означает все интерфейсы.

Защищает ли ufw базу данных в контейнере Docker?

Ненадёжно. Docker маршрутизирует опубликованный трафик контейнеров в таблице nat раньше, чем применяются правила, которыми управляет ufw, поэтому опубликованный порт может оставаться доступным, даже если ufw его запрещает. Публикуйте порт на 127.0.0.1 или фильтруйте через цепочку DOCKER-USER.

Что такое защищённый режим Redis?

Начиная с Redis 3.2.0 защищённый режим включается, когда Redis работает с конфигурацией по умолчанию, привязанным ко всем интерфейсам и без пароля. Тогда он отвечает на запросы только с loopback-адресов, а остальным возвращает ошибку. Это страховка, которую администратор может отключить, а не замена привязке к адресу и аутентификации.

Что делать, если выяснилось, что моя база была открыта?

Считайте, что к данным обращались. Сначала закройте доступ, затем смените все учётные данные баз и приложений, проверьте журналы на незнакомые подключения и восстановите чистую копию, если данные меняли или удаляли. Руководство по реагированию на взлом пошагово разбирает сдерживание и восстановление.

Не уверены, в каком состоянии ваш сервер?

Получите бесплатную проверку безопасности в режиме только чтения или поговорите с инженером по безопасности об управляемой защите.

Получить бесплатную проверку сервера Связаться с инженером по безопасности