Накратко: На жив сайт display_errors = Off принадлежи в php.ini, заедно с display_startup_errors = Off, log_errors = On и лог за грешки извън уеб корена. Освен това CMS и рамките трябва да работят в продукционен режим, иначе собствените им страници с грешки показват подробностите. Заключени с php_admin_flag и проверявани след всяка актуализация, грешките остават в лога, а не в браузъра.
Какво издава едно-единствено съобщение за грешка
Предупреждение в горния край на страницата, „Fatal error" вместо прегледа на поръчката: такива изходи изглеждат безобидни. За атакуващия те са карта. Те показват пълни пътища до файлове, имена на бази данни и таблици, използвани библиотеки и често версията на PHP.
Особено в положения като PHP2Shell, когато масово се търсят уязвими сървъри, един сървър трябва да издава възможно най-малко за себе си. Това не затваря уязвимост в интерпретатора на PHP, но затруднява всяка следваща стъпка след пробив и прави целенасочените атаки срещу Вашето приложение по-трудни.
Към това се добавя защитата на данните. Съобщенията за грешки могат да съдържат части от заявки, например имейл адреси от формуляри. Което се появи в браузъра на посетител, вече не е под Вашия контрол.
Стойностите по подразбиране не са предвидени за продукция
Мнозина предполагат, че PHP сам скрива грешките. Ръководството на PHP казва друго: вградената стойност по подразбиране на display_errors е 1, а display_startup_errors е от PHP 8.0 също включен по подразбиране. Едва доставеният шаблон php.ini-production задава и двете на Off. Ръководството изрично нарича display_errors функция за разработка, която никога не бива да се използва на системи в интернет.
Дали Вашият сървър ползва продукционния шаблон, не е очевидно. Образите на контейнери понякога стартират изцяло без php.ini, хостинг панелите задават собствени стойности, а след преместване обичайно се копира шаблонът за разработка.
display_errors off: преглед на настройките
Следните стойности са смислени за продукционни сървъри. Константи като E_ALL работят само в php.ini; в конфигурацията на Apache те според ръководството на PHP нямат значение и трябва да се задават като число.
| Настройка | Продукция | Цел |
|---|---|---|
display_errors |
Off |
без грешки в браузъра |
display_startup_errors |
Off |
без грешки при стартиране на PHP в изхода |
log_errors |
On |
записване на грешките в протокола |
error_log |
път извън уеб корена | собствен лог файл за всеки сайт или пул |
error_reporting |
E_ALL |
протоколиране на всички грешки, без показване |
html_errors |
Off |
чист текст вместо HTML в протоколите |
zend.exception_ignore_args |
On |
без аргументи на функции в stack trace |
fatal_error_backtraces |
0 до актуализацията (само PHP 8.5) |
преходна мярка според php.net |
zend.exception_ignore_args е особено ценна. Без тази настройка stack trace в логовете и на страниците с грешки могат да съдържат аргументите на извикванията на функции, тоест и пароли или токени, които тъкмо са се обработвали.
; php.ini на уеб SAPI
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/website-fehler.log
error_reporting = E_ALL
html_errors = Off
zend.exception_ignore_args = On
; само PHP 8.5, преходна мярка до актуализацията до 8.5.12:
; fatal_error_backtraces = 0
В пула на FPM заключвате най-важните стойности допълнително, за да не ги включи отново никое приложение чрез ini_set():
; напр. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_flag[display_errors] = off
php_admin_flag[display_startup_errors] = off
php_admin_flag[log_errors] = on
php_admin_value[error_log] = /var/log/php/seite1-fehler.log
След това рестартирайте съответната PHP услуга. Как точно работи заключването, обяснява статията php_admin_value: заключване на настройки срещу презаписване. Пълна основна конфигурация ще намерите в Сигурна конфигурация на php.ini.
Вторият превключвател: режимът за отстраняване на грешки на приложението
PHP е само половината работа. Съвременните рамки сами прихващат грешките и показват собствени, често много подробни страници с грешки. В режим на отстраняване на грешки те съдържат откъси от изходния код, конфигурационни стойности и понякога променливи на средата. Тези страници се появяват дори когато display_errors в PHP е изключен.
Затова проверете за всяко приложение собствения му превключвател. При WordPress това са WP_DEBUG и WP_DEBUG_DISPLAY в wp-config.php, при Laravel и Symfony APP_DEBUG и средата във файла .env, при други системи продукционният режим според документацията на производителя. На жива система режимът за отстраняване на грешки трябва да е изключен трайно, дори „само за кратко за тест" е риск.
Как грешките в приложението се обработват чисто, вместо да се потискат, описва Грешки и изключения в PHP.
Така откривате изхода на грешки, преди други да го направят
Еднократната настройка не е достатъчна. Изходът на грешки се връща след актуализации, преместване или промени в панела. Помага кратък ред на проверка:
- Проверете действащите стойности в уеб контекст, не само в командния ред, например с проверочен файл, поставен за кратко и изтрит веднага след това.
- Проверете режима за отстраняване на грешки на всяко приложение в неговия конфигурационен файл.
- Претърсете върнатите страници за типични текстове на грешки, както в кодовия блок по-долу.
- Потърсете в търсачката със заявка
site:към Вашия домейн за термини като „Fatal error" или „Warning". - Проверете дали лог файлът за грешки се пише и дали не е в уеб корена.
# Претърсване на върнатите страници за текстове на грешки на PHP (само собствени домейни)
for url in https://www.example.de/ https://www.example.de/kontakt/; do
curl -s "$url" | grep -E -o "(Fatal error|Warning|Notice|Deprecated|Stack trace)[^<]{0,80}" | head -3
done
# Пише ли се лог файлът за грешки?
ls -l /var/log/php/
Скриването на грешките не е заместител на тяхното отстраняване. Лог, пълен с предупреждения, е знак за нужда от поддръжка, а след пробив често е важна следа. Какво протоколирате при това и какво не, описва Сигурно протоколиране: записване на грешки без изтичане на данни.
Извод: тихо в браузъра, словоохотливо в лога
Правилото за продукционни сървъри е просто: грешките принадлежат в протокола, не на сайта. На практика то се проваля заради стойности по подразбиране, които не са предвидени за продукция, заради превключватели за отстраняване на грешки в приложенията и заради конфигурации, които се връщат назад след актуализации или преместване.
Който изключи display_errors и display_startup_errors, заключи стойностите в пула, провери режима за отстраняване на грешки на приложението и редовно проверява резултата, затваря един от най-честите източници на ненужно разкриване на информация. Усилието е малко, ефектът трае, докато проверката остава част от всяка актуализация.
Един момент не бива да се изпуска: лог файлът за грешки не е кош за боклук. Който го чете редовно, разпознава неуспешни актуализации, остарели функции и понякога и първите следи от атака, много преди клиентите да забележат нещо.
Как помага PHP SOS конкретно?
- Проверка: прочитаме действащите настройки за грешки за всеки сайт и пул и претърсваме върнатите страници за изход на грешки.
- Превключване: задаваме настройките на PHP и приложението за продукционна работа и ги заключваме срещу презаписване.
- Настройка на протоколите: логовете за грешки са извън уеб корена, ротират се и са достъпни за Вашите разработчици.
- Трайно поддържане: в договора за поддръжка на сигурността проверяваме настройките наново след всяка актуализация.
Често задавани въпроси
Не е ли display_errors в PHP така или иначе изключен?
Не автоматично. Според ръководството на PHP вградената стойност по подразбиране на display_errors е 1. Едва доставеният шаблон php.ini-production задава Off. Дали Вашият сървър ползва този шаблон, зависи от дистрибуцията, панела или образа на контейнера и трябва да се провери.
Достатъчно ли е display_errors = Off, ако WordPress или Laravel работят в режим на отстраняване на грешки?
Не. Много CMS и рамки носят собствени страници с грешки, които могат да показват подробности независимо от PHP. Затова изключете и режима за отстраняване на грешки на приложението, например WP_DEBUG_DISPLAY при WordPress или APP_DEBUG при Laravel и Symfony.
Къде отиват грешките, когато вече не се показват?
С log_errors = On PHP ги записва във файла, посочен от error_log, или в протокола на съответния режим на работа, например лога за грешки на Apache или PHP-FPM. Файлът трябва да е извън уеб корена и да се ротира редовно.
Какво общо има fatal_error_backtraces с PHP2Shell?
Настройката съществува от PHP 8.5 и е активна по подразбиране. php.net посочва fatal_error_backtraces=0 с последващо рестартиране като една от преходните мерки за PHP 8.5, докато актуализацията до 8.5.12 още предстои. Тя не замества актуализацията.