☰
WORDPRESS / PRACTICE

Как хранить секреты WordPress за пределами wp-config.php

Как хранить секреты WordPress за пределами wp-config.php

Файл 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.

← Все материалы WordPress