Файл wp-config.php содержит не только настройки WordPress, но и учетные данные базы данных, SALT-ключи, токены интеграций и другие значения, компрометация которых может предоставить злоумышленнику доступ к сайту или его инфраструктуре. Поэтому хранение этого файла в репозитории вместе с секретами — риск, даже если репозиторий закрытый.
Более безопасная модель — оставить в коде только шаблон конфигурации, а секретные значения передавать во время запуска через переменные окружения, secret manager или отдельный файл, доступный только пользователю веб-сервера. Также важно правильно удалять секреты из истории Git и заменять их после утечки.
Какие данные WordPress нужно защищать
Не все параметры wp-config.php являются секретами. Название сайта, режим отладки или значение WP_HOME могут быть частью конфигурации приложения, но пароли, ключи и токены нужно рассматривать как чувствительные данные.
Особое внимание уделите SALT-ключам и хешам WordPress. Они используются для подписи cookie и защиты сессий. Если эти значения опубликованы, старые пользовательские сессии могут оставаться действительными дольше необходимого, а сами ключи уже нельзя считать надежными.
- DB_PASSWORD, DB_USER и, в зависимости от окружения, DB_HOST и DB_NAME.
- AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY, NONCE_KEY и соответствующие SALT-значения.
- API-токены платежных систем, почтовых сервисов, CRM, CDN и других интеграций.
- SSH-ключи, пароли сторонних сервисов и учетные данные для резервного копирования.
- Секреты в .env, резервных копиях wp-config.php, SQL-дампах и архивах сайта.
Почему приватный Git-репозиторий не решает проблему
Приватный репозиторий снижает риск случайного публичного доступа, но не превращается в специализированное хранилище секретов. К нему могут иметь доступ бывшие сотрудники, CI/CD-системы, подрядчики или скомпрометированные учетные записи.
Главная проблема — история коммитов. Удаление пароля из текущего файла не убирает его из старых коммитов, тегов, форков и кэшей CI. Если секрет хотя бы один раз попал в Git, считайте его скомпрометированным: удалите его из истории и немедленно замените.
Перед публикацией изменений проверяйте staged-файлы. В .gitignore обычно добавляют .env, локальные конфигурации, дампы базы данных, архивы и резервные копии wp-config.php. Однако .gitignore защищает только от будущего добавления файлов и не удаляет то, что уже попало в репозиторий.
- Не полагайтесь на переименование .env.example в .env без проверки его содержимого.
- Не добавляйте в репозиторий wp-config.php.bak, wp-config.php~, backup.sql и архивы сайта.
- Настройте secret scanning на Git-платформе или в CI, но не считайте его заменой ротации ключей.
- Проверяйте логи CI/CD: секреты могут случайно попасть туда из-за команд debug или некорректного вывода переменных.
Вариант с переменными окружения
Переменные окружения позволяют оставить код конфигурации в репозитории, а значения передавать отдельно для development, staging и production. В таком случае wp-config.php не содержит реального пароля базы данных или API-токена.
Пример ниже показывает базовую схему. Значение по умолчанию для DB_HOST может быть удобным локально, но в production лучше явно задать все критические параметры и проверять их наличие во время запуска.
- Храните в репозитории только шаблон вроде .env.example без реальных значений.
- Разделяйте переменные для разных окружений и не используйте production-секреты локально.
- Не выводите переменные окружения на страницу ошибки, в логи или диагностические отчеты.
define('DB_NAME', getenv('WP_DB_NAME'));
define('DB_USER', getenv('WP_DB_USER'));
define('DB_PASSWORD', getenv('WP_DB_PASSWORD'));
define('DB_HOST', getenv('WP_DB_HOST') ?: '127.0.0.1');
define('WP_HOME', getenv('WP_HOME'));
define('WP_SITEURL', getenv('WP_SITEURL'));
define('AUTH_KEY', getenv('WP_AUTH_KEY'));
define('SECURE_AUTH_KEY', getenv('WP_SECURE_AUTH_KEY'));
define('LOGGED_IN_KEY', getenv('WP_LOGGED_IN_KEY'));
define('NONCE_KEY', getenv('WP_NONCE_KEY'));
define('AUTH_SALT', getenv('WP_AUTH_SALT'));
define('SECURE_AUTH_SALT', getenv('WP_SECURE_AUTH_SALT'));
define('LOGGED_IN_SALT', getenv('WP_LOGGED_IN_SALT'));
define('NONCE_SALT', getenv('WP_NONCE_SALT'));
Проверьте, видит ли переменные PHP-FPM
Распространенная ошибка — переменные заданы в shell или контейнере, но PHP-FPM их не получает. В конфигурации пула PHP-FPM параметр clear_env может очищать окружение процесса. Если переменные не передаются, getenv() вернет false или пустое значение, а сайт может завершить работу с ошибкой подключения к базе данных.
Настройки PHP-FPM зависят от операционной системы и способа развертывания. В некоторых окружениях переменные передают через env[...] в конфигурации пула, в контейнерах — через механизмы оркестратора, а в управляемом хостинге такая возможность может быть ограничена. После изменения конфигурации перезапустите PHP-FPM и проверьте результат, не выводя сам секрет.
- Проверьте, под каким пользователем работает PHP-FPM.
- Убедитесь, что необходимые переменные передаются именно worker-процессу PHP, а не только командному shell.
- Проверяйте наличие значения, например его длину или факт успешного подключения, не выводя пароль.
- После изменений проверьте логи PHP-FPM и подключение WordPress к базе данных.
<?php
$name = getenv('WP_DB_NAME');
if ($name === false || $name === '') {
error_log('WP_DB_NAME is not configured');
exit('Configuration error');
}
?>
Отдельный файл конфигурации за пределами web root
Если secret manager или переменные окружения недоступны, секреты можно вынести в отдельный PHP-файл, который находится не в публичной директории сайта. Например, файл может располагаться в каталоге, доступном PHP, но недоступном напрямую через веб-сервер.
Основной wp-config.php подключает этот файл через require_once. Важно использовать абсолютный путь, ограничить права доступа и убедиться, что файл не попадает в резервные копии или репозиторий.
- Владелец файла — пользователь, под которым работает сайт или PHP-FPM.
- Права доступа должны разрешать чтение только необходимому пользователю или группе.
- Каталог с секретами не должен быть доступен для загрузки через Nginx или Apache.
- Не храните его в /tmp: временные каталоги могут иметь небезопасные права или очищаться системой.
- Не копируйте его автоматически в публичный каталог во время деплоя.
require_once '/etc/wordpress/site-secrets.php';
// В site-secrets.php:
define('DB_NAME', 'production_db');
define('DB_USER', 'wordpress');
define('DB_PASSWORD', 'значение_не_должно_быть_в_Git');
define('DB_HOST', '127.0.0.1');
Secret manager и production-развертывание
Для production лучше использовать специализированное хранилище секретов, если это поддерживает ваша инфраструктура. Secret manager контролирует доступ, аудит, ротацию и передачу значений в приложение. Конкретный инструмент зависит от хостинга, облачной платформы или оркестратора.
Наиболее безопасная схема — предоставить приложению доступ только к необходимым секретам, а не хранить единый универсальный пароль администратора инфраструктуры. CI/CD также должен получать минимальные права: например, право прочитать секрет для конкретного деплоя, но не просматривать все секреты проекта.
Если secret manager недоступен, файл с правами доступа только для пользователя веб-сервера может быть приемлемым компромиссом. Однако он не защищает от компрометации самого сервера, поэтому необходимы обновления, изоляция доступа, мониторинг и резервное копирование.
- Ограничьте доступ к секретам по ролям и окружениям.
- Включите аудит чтения и изменения секретов.
- Не передавайте секреты через аргументы командной строки, где они могут оставаться в истории процессов.
- Не встраивайте production-секреты в Docker-образ или артефакт сборки.
- Планируйте ротацию еще до первого инцидента.
Что делать после утечки секретов
Удалить файл из последнего коммита недостаточно. Нужно действовать как при реальном инциденте, даже если неизвестно, успел ли кто-то воспользоваться секретом.
Сначала ограничьте последствия: замените пароль базы данных, отзовите API-токены, сгенерируйте новые SALT-ключи и проверьте активные учетные записи WordPress. После ротации старые значения не должны оставаться действительными.
Затем очистите историю Git с помощью инструмента для переписывания истории, согласуйте это со всеми, у кого есть локальные копии, и принудительно обновите защищенные ветки в соответствии с правилами вашей платформы. Даже после очистки считайте, что старое значение могло быть скопировано.
- Измените пароль пользователя базы данных и проверьте его права.
- Отзовите и перевыпустите токены сторонних сервисов.
- Обновите SALT-ключи WordPress; это обычно приводит к завершению действующих авторизованных сессий.
- Проверьте журналы входов, изменения файлов, новых администраторов и подозрительные задания cron.
- Удалите секрет из истории Git, но не считайте это заменой ротации.
Проверка конфигурации перед запуском
Защита секретов — это не одноразовый перенос значений из wp-config.php. Добавьте проверки в процесс деплоя, чтобы сайт не запускался с пустыми или тестовыми production-значениями.
После развертывания проверьте доступ к базе данных, работу HTTPS, cron, почтовых и платежных интеграций. Во время проверок не выводите полные значения секретов в браузер или логи.
- В репозитории нет .env, SQL-дампов, архивов и резервных копий конфигурации.
- Поиск по словам password, secret, token, api_key и приватным ключам не находит реальных значений.
- PHP-FPM получает все необходимые переменные после перезапуска.
- Права на конфигурационные файлы и каталоги ограничены до минимально необходимых.
- В production отключено публичное отображение ошибок и диагностических данных.
- Существует процедура ротации и понятно, кто отвечает за инцидент.
Частые вопросы
Можно ли оставить wp-config.php в приватном репозитории?
Технически можно, но не стоит хранить там реальные пароли, SALT-ключи и токены. Приватный репозиторий не защищает от утечек через историю Git, CI/CD, доступы сотрудников или резервные копии.
Достаточно ли добавить .env в .gitignore?
Это предотвращает случайное добавление нового локального файла, но не удаляет его из истории, если он уже был закоммичен. В таком случае секреты нужно заменить, а историю репозитория — очистить.
Почему getenv() возвращает пустое значение в WordPress?
Чаще всего PHP-FPM не получает переменные окружения или очищает их параметром clear_env. Проверьте конфигурацию пула, способ запуска контейнера или настройки хостинга и перезапустите PHP-FPM после изменений.
Нужно ли менять SALT-ключи после утечки wp-config.php?
Да. После утечки их следует сгенерировать заново. Это делает старые ключи непригодными и обычно завершает активные пользовательские сессии, поэтому ротацию нужно планировать с учетом этого эффекта.
Вывод
Безопасный wp-config.php не должен быть хранилищем всех секретов проекта. Оставьте в коде только логику подключения конфигурации, а значения передавайте через переменные окружения, secret manager или защищенный файл за пределами web root.
Добавьте проверки в процесс деплоя, ограничьте права доступа, не коммитьте резервные копии и регулярно просматривайте историю изменений. Если секрет уже утек, сначала замените его, а затем очищайте историю Git.