Krótko: Na działającej stronie display_errors = Off należy do php.ini, razem z display_startup_errors = Off, log_errors = On i logiem błędów poza katalogiem głównym strony. Dodatkowo systemy CMS i frameworki muszą działać w trybie produkcyjnym, w przeciwnym razie ich własne strony błędów pokazują szczegóły. Zablokowane przez php_admin_flag i sprawdzane po każdej aktualizacji, błędy zostają w logu, a nie w przeglądarce.
Co zdradza pojedynczy komunikat błędu
Ostrzeżenie na górze strony, „Fatal error” zamiast podsumowania zamówienia: takie wyświetlenia wyglądają niewinnie. Dla atakującego są mapą. Pokazują pełne ścieżki plików, nazwy baz danych i tabel, używane biblioteki, a często także wersję PHP.
Właśnie w sytuacjach takich jak PHP2Shell, gdy masowo szuka się podatnych serwerów, serwer powinien zdradzać o sobie jak najmniej. Nie zamyka to luki w interpreterze PHP, ale utrudnia każdy kolejny krok po włamaniu i czyni celowane ataki na Państwa aplikację mozolniejszymi.
Dochodzi ochrona danych. Komunikaty błędów mogą zawierać fragmenty żądań, na przykład adresy e-mail z formularzy. To, co pojawia się w przeglądarce odwiedzającego, nie jest już pod Państwa kontrolą.
Wartości domyślne nie są przeznaczone do produkcji
Wielu zakłada, że PHP samo ukrywa błędy. Podręcznik PHP mówi coś innego: wbudowana wartość domyślna display_errors to 1, a display_startup_errors jest od PHP 8.0 również domyślnie włączone. Dopiero dołączony szablon php.ini-production ustawia oba na Off. Podręcznik wyraźnie nazywa display_errors funkcją deweloperską, której nigdy nie należy używać w systemach dostępnych w internecie.
To, czy Państwa serwer używa szablonu produkcyjnego, nie jest oczywiste. Obrazy kontenerów startują częściowo w ogóle bez php.ini, panele hostingu ustawiają własne wartości, a po przeprowadzce chętnie kopiuje się szablon deweloperski.
display_errors off: ustawienia w skrócie
Poniższe wartości są sensowne dla serwerów produkcyjnych. Stałe takie jak E_ALL działają tylko w php.ini; w konfiguracji Apache nie mają według podręcznika PHP znaczenia i trzeba podawać je jako liczbę.
| Ustawienie | Produkcja | Cel |
|---|---|---|
display_errors |
Off |
brak błędów w przeglądarce |
display_startup_errors |
Off |
brak błędów przy starcie PHP w wyjściu |
log_errors |
On |
zapisywanie błędów do dziennika |
error_log |
ścieżka poza katalogiem głównym strony | osobny plik logu dla każdej strony lub puli |
error_reporting |
E_ALL |
rejestrować wszystkie błędy, nie wyświetlać |
html_errors |
Off |
zwykły tekst zamiast HTML w dziennikach |
zend.exception_ignore_args |
On |
brak argumentów funkcji w śladach stosu |
fatal_error_backtraces |
0 do aktualizacji (tylko PHP 8.5) |
środek przejściowy według php.net |
zend.exception_ignore_args jest szczególnie cenne. Bez tego ustawienia ślady stosu w logach i na stronach błędów mogą zawierać argumenty wywołań funkcji, a więc także hasła lub tokeny, które właśnie były przetwarzane.
; php.ini SAPI WWW
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
; tylko PHP 8.5, środek przejściowy do aktualizacji do 8.5.12:
; fatal_error_backtraces = 0
W puli FPM najważniejsze wartości blokuje się dodatkowo, aby żadna aplikacja nie włączyła ich ponownie przez ini_set():
; np. /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
Następnie zrestartować odpowiednią usługę PHP. Jak dokładnie działa blokowanie, wyjaśnia artykuł php_admin_value: blokowanie ustawień przed nadpisaniem. Pełną konfigurację podstawową znajdą Państwo w artykule Bezpieczna konfiguracja php.ini.
Drugi przełącznik: tryb debugowania aplikacji
PHP to tylko połowa sprawy. Nowoczesne frameworki same przechwytują błędy i pokazują własne, często bardzo szczegółowe strony błędów. W trybie debugowania zawierają fragmenty kodu źródłowego, wartości konfiguracji, a czasem zmienne środowiskowe. Strony te pojawiają się nawet wtedy, gdy display_errors w PHP jest wyłączone.
Dlatego proszę sprawdzić dla każdej aplikacji jej własny przełącznik. W WordPress są to WP_DEBUG i WP_DEBUG_DISPLAY w wp-config.php, w Laravel i Symfony APP_DEBUG oraz środowisko w pliku .env, w innych systemach tryb produkcyjny według dokumentacji producenta. W systemie produkcyjnym tryb debugowania ma być wyłączony na stałe, także „tylko na chwilę dla testu” jest ryzykiem.
Jak w aplikacji poprawnie obsługiwać błędy, zamiast je tłumić, opisuje artykuł Błędy i wyjątki w PHP.
Jak znaleźć wyświetlane błędy, zanim zrobią to inni
Jednorazowe ustawienie nie wystarczy. Wyświetlanie błędów wraca po aktualizacjach, przeprowadzkach lub zmianach w panelu. Pomaga krótki przebieg kontrolny:
- Sprawdzić obowiązujące wartości w kontekście WWW, nie tylko w wierszu poleceń, na przykład krótko umieszczonym i zaraz usuniętym plikiem testowym.
- Skontrolować tryb debugowania każdej aplikacji w jej pliku konfiguracyjnym.
- Przeszukać dostarczane strony pod kątem typowych tekstów błędów, jak w bloku kodu poniżej.
- W wyszukiwarce zapytaniem
site:na Państwa domenę szukać pojęć takich jak „Fatal error” lub „Warning”. - Sprawdzić, czy log błędów jest zapisywany i czy nie leży w katalogu głównym strony.
# Przeszukać dostarczane strony pod kątem tekstów błędów PHP (tylko własne domeny)
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
# Czy log błędów jest zapisywany?
ls -l /var/log/php/
Ukrywanie błędów nie zastępuje ich naprawiania. Log pełen ostrzeżeń to wskazówka, że potrzebna jest konserwacja, a po włamaniu jest często ważnym śladem. Co przy tym rejestrować, a czego nie, opisuje artykuł Bezpieczne logowanie: rejestrowanie błędów bez wycieku danych.
Wniosek: cicho w przeglądarce, wylewnie w logu
Zasada dla serwerów produkcyjnych jest prosta: błędy należą do dziennika, a nie na stronę. W praktyce przeszkadzają jej wartości domyślne nieprzeznaczone do produkcji, przełączniki debugowania w aplikacjach i konfiguracje, które po aktualizacjach lub przeprowadzkach wracają do stanu pierwotnego.
Kto wyłącza display_errors i display_startup_errors, blokuje wartości w puli, kontroluje tryb debugowania aplikacji i regularnie sprawdza wynik, zamyka jedno z najczęstszych źródeł niepotrzebnego ujawniania informacji. Nakład jest niewielki, a efekt trwa, dopóki kontrola pozostaje częścią każdej aktualizacji.
Jednego nie należy przeoczyć: log błędów nie jest koszem na śmieci. Kto go regularnie czyta, rozpoznaje błędne aktualizacje, przestarzałe funkcje, a czasem także pierwsze ślady ataku, na długo zanim klienci cokolwiek zauważą.
Jak konkretnie pomaga PHP SOS?
- Sprawdzenie: odczytujemy obowiązujące ustawienia błędów dla każdej strony i puli oraz przeszukujemy dostarczane strony pod kątem wyświetlanych błędów.
- Przestawienie: ustawiamy PHP i aplikacje na tryb produkcyjny i blokujemy je przed nadpisaniem.
- Konfiguracja dzienników: logi błędów leżą poza katalogiem głównym strony, są rotowane i dostępne dla Państwa programistów.
- Utrzymanie na stałe: w ramach umowy serwisowej bezpieczeństwa po każdej aktualizacji sprawdzamy ustawienia ponownie.
Częste pytania
Czy display_errors w PHP nie jest i tak wyłączone?
Nie automatycznie. Według podręcznika PHP wbudowana wartość domyślna display_errors to 1. Dopiero dołączony szablon php.ini-production ustawia Off. To, czy Państwa serwer używa tego szablonu, zależy od dystrybucji, panelu lub obrazu kontenera i należy to sprawdzić.
Czy display_errors = Off wystarczy, gdy WordPress lub Laravel działają w trybie debugowania?
Nie. Wiele systemów CMS i frameworków ma własne strony błędów, które niezależnie od PHP mogą wyświetlać szczegóły. Dlatego należy też wyłączyć tryb debugowania aplikacji, na przykład WP_DEBUG_DISPLAY w WordPress lub APP_DEBUG w Laravel i Symfony.
Dokąd trafiają błędy, gdy nie są już wyświetlane?
Przy log_errors = On PHP zapisuje je do pliku wskazanego przez error_log lub do dziennika danego trybu pracy, na przykład logu błędów Apache lub PHP-FPM. Plik powinien leżeć poza katalogiem głównym strony i być regularnie rotowany.
Co fatal_error_backtraces ma wspólnego z PHP2Shell?
Ustawienie istnieje od PHP 8.5 i jest domyślnie aktywne. php.net podaje fatal_error_backtraces=0 z następującym po tym restartem jako jeden ze środków przejściowych dla PHP 8.5, dopóki aktualizacja do 8.5.12 jest jeszcze w toku. Nie zastępuje aktualizacji.