Серверы
Безопасность базы данных: от порта до шифрования полей
База не должна быть открыта в интернет; пользователи с минимальными правами, TLS, резервные копии, шифрование чувствительных полей и аудит. Практика для PostgreSQL и MySQL.
· 11 мин. чтения

База данных — самая ценная часть системы. Пользователи, платежи, документы, персональные данные — всё хранится здесь. Тем не менее базу часто устанавливают по принципу «приложение работает, значит всё в порядке»: настройки по умолчанию, один пользователь root или postgres, прослушивание на всех сетевых интерфейсах.
В этой статье разберём основные слои защиты базы: сеть, пользователи и права, шифрованное соединение, шифрование чувствительных полей, резервное копирование и аудит. Примеры — для PostgreSQL и MySQL/MariaDB, сторона приложения — на примере Laravel и Node.
В чём проблема
Самые частые ошибки с базами данных:
- Порт открыт в интернет. MySQL (3306), PostgreSQL (5432), Redis (6379), MongoDB (27017) видны снаружи. Это прямой путь для подбора паролей и эксплуатации уязвимостей в самой СУБД.
- Приложение подключается под суперпользователем. Если в приложении используется
rootилиpostgres, одна SQL-инъекция может дать управление всем сервером: другие базы, чтение файлов, выполнение команд. - Слабые или повторяющиеся пароли. Один и тот же пароль в нескольких средах, файл
.envостался в истории git. - Нешифрованное соединение. Если приложение и база на разных серверах, запросы и результаты идут по сети открытым текстом.
- Чувствительные поля хранятся открыто. Паспортные данные, ПИНФЛ, токены — обычным текстом. Любая копия базы — полная утечка.
- Открыт веб-интерфейс базы данных. Инструменты вроде phpMyAdmin или Adminer доступны по публичному адресу.
Почему это важно для экспертизы
На экспертизе сервер сканируется снаружи по портам — открытый порт базы обнаруживается сразу. На экспертизе нашего собственного продукта открытые порты тоже были отдельной находкой (там это были порты FTP и панели), подробнее: открытые порты и харденинг сервера. Кроме того:
- адреса вроде
/phpmyadmin,/adminer.php,/pmaпроверяются по типичным именам; - если найдена SQL-инъекция, её последствия зависят от прав пользователя в базе — минимальные права ограничивают ущерб;
- категория A02 «Cryptographic Failures» в OWASP Top 10 (2021) охватывает отсутствие шифрования при хранении и передаче чувствительных данных, а A05 «Security Misconfiguration» — настройки по умолчанию.
Принцип: база видна только приложению, приложение может делать только необходимое, а чувствительные данные нельзя прочитать, даже если они утекут из базы.
Проверьте сами
Где слушает база
Внутри сервера:
sudo ss -tlnp | grep -E "3306|5432|6379|27017"
127.0.0.1 или Unix-сокет — хорошо. 0.0.0.0 или * — при отсутствии firewall база открыта снаружи.
Снаружи (с другого компьютера, только для своего сервера):
nmap -Pn -p 3306,5432,6379,27017 your-server-ip
Пользователи и права
PostgreSQL:
SELECT rolname, rolsuper, rolcreaterole, rolcreatedb FROM pg_roles WHERE rolcanlogin;
MySQL:
SELECT user, host FROM mysql.user;
SHOW GRANTS FOR 'app'@'localhost';
У пользователя приложения не должно быть SUPER, FILE, GRANT OPTION или ALL PRIVILEGES ON *.*. Если в столбце host стоит %, пользователь может подключиться с любого адреса.
Веб-интерфейсы
for p in phpmyadmin pma adminer.php adminer phpMyAdmin; do
printf "%s " "$p"; curl -s -o /dev/null -w "%{http_code}\n" "https://example.uz/$p/"
done
Исправление
1. Привяжите базу к локальному адресу
PostgreSQL postgresql.conf:
listen_addresses = 'localhost'
MySQL/MariaDB mysqld.cnf:
[mysqld]
bind-address = 127.0.0.1
Redis redis.conf: bind 127.0.0.1 ::1 и protected-mode yes, а также пароль или ACL.
Если база на отдельном сервере, она должна слушать только внутреннюю сеть, а firewall — разрешать доступ только с IP сервера приложения:
sudo ufw allow from 10.0.0.5 to any port 5432 proto tcp
В PostgreSQL по тому же принципу настраивается pg_hba.conf:
# TYPE DATABASE USER ADDRESS METHOD
local appdb app scram-sha-256
hostssl appdb app 10.0.0.5/32 scram-sha-256
Не используйте метод trust, для хеширования паролей выберите scram-sha-256 (password_encryption = scram-sha-256).
2. Пользователи с минимальными правами
Разделите как минимум три роли: миграции (меняют схему), приложение (работает только с данными) и резервное копирование или отчёты (только чтение).
PostgreSQL:
CREATE ROLE app LOGIN PASSWORD '...';
REVOKE ALL ON SCHEMA public FROM PUBLIC;
GRANT USAGE ON SCHEMA public TO app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app;
ALTER DEFAULT PRIVILEGES FOR ROLE migrator IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app;
MySQL:
CREATE USER 'app'@'localhost' IDENTIFIED BY '...';
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app'@'localhost';
CREATE USER 'report'@'localhost' IDENTIFIED BY '...';
GRANT SELECT ON appdb.* TO 'report'@'localhost';
Генерируйте пароли через openssl rand -base64 24 и храните их только в .env или менеджере секретов. Для каждой среды (тест, production) — свой пароль. Подробнее: управление секретами и ключами.
3. Шифрованное соединение (TLS)
Если база и приложение на одном сервере и подключение идёт через Unix-сокет или 127.0.0.1, TLS не обязателен. Если на разных — обязателен.
На сервере PostgreSQL настраиваются ssl = on и сертификат, в pg_hba.conf используется hostssl. Laravel config/database.php:
'pgsql' => [
// ...
'sslmode' => env('DB_SSLMODE', 'verify-full'),
],
В MySQL можно обязать пользователя подключаться только по TLS:
ALTER USER 'app'@'10.0.0.5' REQUIRE SSL;
Пример для Node.js (pg):
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: { ca: fs.readFileSync('/etc/ssl/db-ca.pem'), rejectUnauthorized: true },
});
rejectUnauthorized: false отключает проверку сертификата, и TLS теряет смысл.
4. Шифрование чувствительных полей
Шифрование диска защищает при краже сервера, но не защищает данные, полученные через дамп базы или SQL-инъекцию. Для самых чувствительных полей добавьте шифрование на уровне приложения.
В Laravel — cast encrypted:
protected function casts(): array
{
return [
'passport_number' => 'encrypted',
'api_secret' => 'encrypted',
];
}
Если нужен поиск по зашифрованному полю, храните в отдельном столбце ключевой хеш (HMAC) и ищите по нему. Пароли же не шифруются, а хешируются через bcrypt или argon2id.
Важно: ключ шифрования (APP_KEY) хранится отдельно от базы. Если ключ и база утекут вместе, шифрование не поможет. План смены ключа тоже нужно продумать заранее.
5. Закройте веб-интерфейсы
Если phpMyAdmin или Adminer нужны, не оставляйте их по публичному адресу: только через SSH-туннель или allowlist. Лучше всего вообще не устанавливать их на production-сервер, а при необходимости подключаться локальным клиентом через SSH-туннель:
ssh -L 5432:127.0.0.1:5432 user@your-server
6. Резервные копии и аудит
Резервная копия базы должна быть зашифрована и храниться в другом месте, а восстановление — регулярно проверяться; подробно об этом в статье резервные копии и восстановление.
Для аудита:
- в PostgreSQL
log_connections = on,log_disconnections = on, для изменений схемыlog_statement = 'ddl'; для детального аудита — расширениеpgaudit; - в MySQL — логирование неудачных подключений, в Percona или MariaDB — плагин аудита;
- на уровне приложения — кто просматривал или изменял чувствительную запись (таблица аудита).
Логирование всех запросов (log_statement = 'all') занимает много места и приводит к попаданию чувствительных данных в логи — включайте его только для временной диагностики.
7. Обновления
СУБД тоже нужно обновлять. Версии с истёкшим сроком поддержки не получают исправлений безопасности. Проверьте версию (SELECT version();) и сверьте её с графиком поддержки производителя.
На стороне приложения
Настройки базы не могут полностью закрыть ошибку в приложении. Основные правила:
- только параметризованные запросы, никакой конкатенации значений в
whereRawи сыром SQL; - allowlist для
orderByи имён столбцов; - не показывать SQL-запросы и детали базы в сообщениях об ошибках (
APP_DEBUG=false); - никогда не помещать строку подключения во front-end или мобильное приложение.
Короткий чек-лист
- Порты базы закрыты из интернета, база слушает
127.0.0.1или внутреннюю сеть pg_hba.conf/hostв MySQL разрешают только нужные адреса, нетtrust- Приложение подключается не под суперпользователем, а под пользователем с минимальными правами
- Отдельные роли для миграций, приложения и отчётов
- Сильные пароли, свои для каждой среды;
.envнет в git - TLS и проверка сертификата между разными серверами
- Самые чувствительные поля зашифрованы на уровне приложения, ключ хранится отдельно
- phpMyAdmin/Adminer не доступны по публичному адресу
- Зашифрованные резервные копии и тест восстановления
- Подключения и изменения схемы логируются, версия СУБД поддерживается
Итог
Защита базы состоит из нескольких простых слоёв: скрыть её из сети, ограничить права, зашифровать соединение и чувствительные поля, делать копии и вести наблюдение. Каждый слой ограничивает ущерб, если другой не сработал. В нашем процессе Claude AI агент просматривает конфигурацию базы, pg_hba.conf, права пользователей и запросы в коде приложения, а специалист применяет изменения в безопасном порядке, не останавливая работающую систему.
Если хотите узнать, готова ли ваша база к экспертизе, оставьте заявку на сканирование. Мы поможем закрыть уязвимости до официальной проверки.


