Навіщо хардити PHP
PHP має безліч функцій, небезпечних за неправильної конфігурації. Правильне налаштування php.ini закриває цілі класи вразливостей: віддалене виконання коду (RCE), розкриття інформації про сервер, перебір файлів через обходи open_basedir. Це перший крок захисту будь-якого PHP-сервера.
Приховати інформацію про PHP
expose_php = Off
; Прибирає заголовок X-Powered-By: PHP/8.x з HTTP-відповідейВимкнути небезпечні функції
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,\
curl_exec,curl_multi_exec,parse_ini_file,show_source,\
phpinfo,posix_kill,posix_mkfifo,posix_setpgidОбмеження доступу до файлової системи
open_basedir = /var/www/mysite:/tmp:/usr/share/php
; Забороняє PHP читати файли за межами вказаних директорійВимкнути віддалені URL
allow_url_fopen = Off
allow_url_include = Off
; Блокує file_get_contents("http://...") та include з віддалених URLБезпечні налаштування сесій
session.cookie_httponly = On
session.cookie_secure = On
session.cookie_samesite = Strict
session.use_strict_mode = On
session.use_only_cookies = On
session.gc_maxlifetime = 1440Налаштування завантаження файлів
file_uploads = On
upload_tmp_dir = /tmp
upload_max_filesize = 64M
; Обмежте upload_tmp_dir правами www-dataВимкнути register_globals і magic_quotes (PHP < 5.4)
; Застарілі, але перевірте у старих конфігах:
register_globals = Off
magic_quotes_gpc = OffЧекліст безпеки PHP
expose_php = Off— приховати версію PHPdisplay_errors = Off— не показувати помилки відвідувачамlog_errors = On— записувати помилки у файлallow_url_include = Off— заборонити віддалені includedisable_functions— вимкнути небезпечні функціїsession.cookie_httponly = On— захист сесійopen_basedir— обмежити файлову систему
# Застосувати зміни
sudo systemctl reload php8.2-fpm
# Перевірка
php -r "phpinfo();" 2>&1 | grep expose_phpcomposer require enlightn/security-checker для автоматичної перевірки відомих вразливостей у залежностях.Реальні помилки після хардингу
Функцію вимкнено через disable_functions
Warning: shell_exec() has been disabled for security reasons in /var/www/html/deploy.php on line 22Це попередження, а не фатальна помилка: виклик мовчки повертає NULL, виконання скрипта триває далі. Код, що не перевіряє результат shell_exec() на null, далі падає вже з іншою, менш зрозумілою помилкою — налагодження ускладнюється вдвічі.
allow_url_fopen заблокував запит
Warning: file_get_contents(http://api.example.com/data): Failed to open stream: no suitable wrapper could be found in /var/www/html/app/Api.php on line 8Після allow_url_fopen = Off функції на кшталт file_get_contents() та fopen() перестають відкривати мережеві адреси — це очікувано й правильно, але код, що читає зовнішні API через них, потрібно переписати на cURL заздалегідь, до викочування хардингу на прод.
Порушення open_basedir
Warning: file_get_contents(): open_basedir restriction in effect. File(/etc/passwd) is not within the allowed path(s): (/var/www/mysite:/tmp:/usr/share/php) in /var/www/mysite/app/Reader.php on line 5Це рівно та поведінка, заради якої директиву й вмикали — спроба вийти за межі дозволених шляхів блокується ще до системного виклику. Якщо легітимний код отримує цю помилку на шляхах, які мають бути доступними, — розширте перелік через двокрапку, а не вимикайте директиву повністю.
Коли хардинг ламає легітимні інструменти
disable_functionsізproc_openтаpopenламає WP-CLI, деплой-скрипти через Composer scripts і будь-які інструменти, що запускають зовнішні процеси — перевіряйте перелік використовуваних утиліт перед вимкненням.- Глобальний
open_basedirламає симлінки на спільні бібліотеки за межами дозволених шляхів — часта проблема при shared-vendor між кількома проектами. session.cookie_secure = Onбез HTTPS залишить користувачів узагалі без робочих сесій — куки просто не надсилатимуться браузером.
Перевірка результату
# Версія PHP більше не світиться в заголовках відповіді
curl -I https://example.com | grep -i x-powered-by
# (порожньо — означає, що expose_php спрацював)
# Перелік фактично вимкнених функцій
php -i | grep disable_functions
# Перевірка конкретної небезпечної функції з коду
php -r "var_dump(function_exists('exec'));"Відсутність заголовка X-Powered-By у відповіді curl — надійна ознака, що expose_php = Off застосувався. Для перевірки безпечного завантаження файлів додатково протестуйте реальне надсилання файлу через форму, а не лише налаштування php.ini.
Регулярно повторюйте цю перевірку після кожного оновлення PHP: нова мінорна версія іноді повертає директиву до значення за замовчуванням, якщо вона була задана лише в окремому модульному ini-файлі, який оновлення пакета перезаписало.