☰
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-ключі, паролі до сторонніх сервісів і credentials для резервного копіювання.
  • Секрети в .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 за допомогою інструмента для переписування історії, узгодьте це з усіма, хто має локальні копії, і примусово оновіть захищені гілки за правилами вашої платформи. Навіть після очищення вважайте, що старе значення могло бути скопійоване.

  • Змініть пароль користувача бази та перевірте його права.
  • Відкличте й перевипустіть токени сторонніх сервісів.
  • Оновіть WordPress SALT-ключі; це зазвичай призводить до завершення чинних авторизованих сесій.
  • Перевірте журнали входів, зміни файлів, нових адміністраторів і підозрілі завдання 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