Зачем хардить 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-файле, который апдейт пакета перезаписал.