До основного вмісту

Безпечна конфігурація PHP: налаштування php.ini для продакшн

Безпека · 19.04.2026 · 4 хв читання
Ілюстрація до статті «Безпечна конфігурація PHP: налаштування php.ini для продакшн»

Налаштування PHP за замовчуванням оптимізовані для розробки, а не для продакшн. На публічному сервері вони розкривають версію PHP і внутрішні шляхи, дозволяють небезпечні операції.

Знайти php.ini

php --ini | grep "Loaded Configuration"
# /etc/php/8.3/fpm/php.ini

У PHP-FPM і PHP CLI різні файли конфігурації навіть в одній системі — зазвичай /etc/php/8.3/fpm/php.ini і /etc/php/8.3/cli/php.ini. Правки в одному не застосовуються до іншого: налаштування для cron-завдань і консольних скриптів потрібно змінювати окремо.

Після правки будь-якого php.ini зміни не підхоплюються автоматично — PHP-FPM читає файл один раз при старті і тримає його в пам'яті воркера. Забутий перезапуск сервісу — часта причина, через яку «налаштування ніби застосоване», але перевірка показує старе значення.

sudo systemctl restart php8.3-fpm

Обов'язкові налаштування безпеки

display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/errors.log
expose_php = Off

open_basedir = /var/www/site/:/tmp/:/var/lib/php/sessions/

disable_functions = exec,passthru,shell_exec,system,proc_open,popen,phpinfo

file_uploads = On
upload_max_filesize = 10M
max_file_uploads = 5
max_execution_time = 30
memory_limit = 256M

Розбір помилок конфігурації

display_errors = On на продакшні

Якщо в цьому налаштуванні залишився On, будь-яка необроблена помилка PHP виводиться прямо у HTML-відповідь і видна кожному відвідувачу:

Fatal error: Uncaught PDOException: SQLSTATE[HY000] [1045]
Access denied for user 'app'@'localhost' in /var/www/site/db.php:12
Stack trace:
#0 /var/www/site/index.php(5): connect()
#1 {main}
  thrown in /var/www/site/db.php on line 12

Таке повідомлення розкриває повний шлях до файлів на сервері, ім'я користувача бази даних і структуру коду — цього достатньо, щоб спланувати наступний крок атаки. Помилки мають потрапляти в error_log, а не в браузер.

open_basedir налаштований лише для FPM

Якщо обмеження додано в /etc/php/8.3/fpm/php.ini, але не в cli/php.ini, консольні скрипти і cron-завдання виконуються без нього. Атака через веб-форму, яка передає дані у фонове завдання на CLI, обходить захист, розрахований лише на веб-запити.

У кожного віртуального хоста на спільному сервері може бути власний пул PHP-FPM зі своїм файлом налаштувань — якщо правите загальний php.ini, але конкретний сайт запущений через пул з окремим php_admin_value, що перевизначає потрібний параметр, зміна в загальному файлі не подіє саме на цей сайт.

Що змінює кожне налаштування

ПараметрЗначення за замовчуваннямЩо дає хардненне налаштування
expose_phpOnПрибирає версію PHP із заголовка відповіді сервера
display_errorsOnПомилки йдуть у лог, а не у HTML-відповідь відвідувачу
open_basedirне заданийPHP не може читати файли поза вказаними директоріями
disable_functionsпорожньоБлокує запуск shell-команд із PHP-коду

Безпека сесій

session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = Strict
session.use_only_cookies = 1
session.use_strict_mode = 1
session.gc_maxlifetime = 1440
session.save_path = /var/lib/php/sessions

Перевірка конфігурації

php -r "echo ini_get('expose_php');"
php -r "echo ini_get('display_errors');"

Перевірте також бойове середовище, а не лише CLI: запросіть заголовки реального сайту і переконайтеся, що версія PHP не розкривається веб-сервером.

curl -I https://ваш-домен/ | grep -i x-powered-by
# Порожній вивід — заголовок не віддається, все вірно
open_basedir обмежує PHP доступом до файлів поза вказаними директоріями. Це блокує атаки Path Traversal і LFI (Local File Inclusion), коли зловмисник намагається прочитати /etc/passwd.
disable_functions можна обійти через PHP-розширення. Це додатковий шар захисту, але не заміна правильному коду і регулярним оновленням.

Коли жорсткі налаштування заважають роботі

  • Застосунку для обробки зображень чи відео потрібен зовнішній бінарник через shell_exec (наприклад, виклик окремого конвертера) — тоді з disable_functions прибирають лише конкретно потрібну функцію, а не залишають список порожнім повністю.
  • Спільний хостинг із кількома сайтами під різними користувачами на одному PHP-FPM пулі — єдиний open_basedir для всіх не підійде, кожному віртуальному хосту потрібен власний FPM-пул зі своїм значенням.
  • Легасі-код, який читає файли за межами каталогу сайту (наприклад, зі спільного каталогу з шаблонами) — такий код потрібно виправляти, розширення open_basedir «про всяк випадок» повертає ту саму вразливість, від якої він захищає.

Налаштування веб-сервера перед PHP-FPM розбирає стаття хардненинг Nginx, а захист самого WordPress поверх цих налаштувань — хардненинг WordPress.

Чи була стаття корисною?
← Назад до бази знань Поставити питання підтримці