Ukratko: Na živoj web-stranici display_errors = Off pripada u php.ini, zajedno s display_startup_errors = Off, log_errors = On i zapisnikom pogrešaka izvan web-korijena. Uz to CMS-ovi i frameworki moraju raditi u produkcijskom načinu, inače njihove vlastite stranice s pogreškama prikazuju pojedinosti. Zaključane pomoću php_admin_flag i provjeravane nakon svakog ažuriranja, pogreške ostaju u zapisniku umjesto u pregledniku.
Što odaje jedna jedina poruka o pogrešci
Upozorenje na vrhu stranice, „Fatal error“ umjesto pregleda narudžbe: takvi ispisi izgledaju bezazleno. Za napadača su karta. Pokazuju potpune putove datoteka, nazive baza podataka i tablica, korištene biblioteke i često verziju PHP-a.
Osobito u situacijama poput PHP2Shella, u kojima se masovno traže ranjivi poslužitelji, poslužitelj bi trebao odavati što je moguće manje o sebi. To ne zatvara ranjivost u PHP interpreteru, ali otežava svaki daljnji korak nakon provale i čini ciljane napade na Vašu aplikaciju mukotrpnijima.
Tu je i zaštita podataka. Poruke o pogreškama mogu sadržavati dijelove zahtjeva, primjerice e-adrese iz obrazaca. Što se pojavi u pregledniku posjetitelja, više nije pod Vašom kontrolom.
Zadane vrijednosti nisu zamišljene za produkciju
Mnogi pretpostavljaju da PHP pogreške sam skriva. PHP priručnik kaže drugo: ugrađena zadana vrijednost display_errors je 1, a display_startup_errors je od PHP-a 8.0 također zadano uključen. Tek isporučeni predložak php.ini-production postavlja oboje na Off. Priručnik display_errors izričito naziva razvojnom funkcijom koju nikada ne treba koristiti na sustavima na internetu.
Koristi li Vaš poslužitelj produkcijski predložak, nije samorazumljivo. Image-i kontejnera katkad se pokreću posve bez php.ini, hosting paneli postavljaju vlastite vrijednosti, a nakon selidbe rado se kopira razvojni predložak.
display_errors off: pregled postavki
Sljedeće vrijednosti smislene su za produkcijske poslužitelje. Konstante poput E_ALL rade samo u php.ini; u Apache konfiguraciji prema PHP priručniku nemaju značenje i moraju se navesti kao broj.
| Postavka | Produkcija | Svrha |
|---|---|---|
display_errors |
Off |
nema pogrešaka u pregledniku |
display_startup_errors |
Off |
nema pogrešaka pri pokretanju PHP-a u izlazu |
log_errors |
On |
pogreške upisivati u zapisnik |
error_log |
putanja izvan web-korijena | zasebna zapisnička datoteka po web-stranici ili poolu |
error_reporting |
E_ALL |
zapisivati sve pogreške, ne prikazivati |
html_errors |
Off |
običan tekst umjesto HTML-a u zapisnicima |
zend.exception_ignore_args |
On |
bez argumenata funkcija u stack traceovima |
fatal_error_backtraces |
0 do ažuriranja (samo PHP 8.5) |
prijelazna mjera prema php.net-u |
zend.exception_ignore_args osobito je vrijedan. Bez te postavke stack traceovi u zapisnicima i na stranicama s pogreškama mogu sadržavati argumente poziva funkcija, dakle i lozinke ili tokene koji su upravo obrađivani.
; php.ini web SAPI-ja
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
; samo PHP 8.5, prijelazna mjera do ažuriranja na 8.5.12:
; fatal_error_backtraces = 0
U FPM poolu najvažnije vrijednosti dodatno zaključajte kako ih nijedna aplikacija ne bi putem ini_set() ponovno uključila:
; npr. /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
Zatim ponovno pokrenite dotičnu PHP uslugu. Kako zaključavanje točno funkcionira, objašnjava članak php_admin_value: zaključati postavke od nadjačavanja. Potpunu osnovnu konfiguraciju naći ćete u članku Sigurno konfigurirati php.ini.
Druga sklopka: debug način aplikacije
PHP je tek pola posla. Moderni frameworki sami hvataju pogreške i prikazuju vlastite, često vrlo opširne stranice s pogreškama. U debug načinu sadrže izvatke izvornog koda, konfiguracijske vrijednosti i ponekad varijable okruženja. Te se stranice pojavljuju čak i kad je display_errors u PHP-u isključen.
Zato za svaku aplikaciju provjerite njezinu vlastitu sklopku. Kod WordPressa to su WP_DEBUG i WP_DEBUG_DISPLAY u wp-config.php, kod Laravela i Symfonyja APP_DEBUG i okruženje u datoteci .env, kod drugih sustava produkcijski način prema dokumentaciji proizvođača. Na živom sustavu debug način trajno pripada isključen, a i „samo načas za testiranje“ je rizik.
Kako se pogreške u aplikaciji uredno obrađuju umjesto da se potiskuju, opisuje članak Pogreške i iznimke u PHP-u.
Kako pronaći ispise pogrešaka prije nego ih nađu drugi
Jednokratno postavljanje nije dovoljno. Ispisi pogrešaka vraćaju se nakon ažuriranja, selidbi ili izmjena u panelu. Pomaže kratak postupak provjere:
- Provjeriti djelotvorne vrijednosti u web-kontekstu, a ne samo u naredbenom retku, primjerice kratko odloženom datotekom za provjeru koja se odmah zatim briše.
- Provjeriti debug način svake aplikacije u njezinoj konfiguracijskoj datoteci.
- Isporučene stranice pretražiti na tipične tekstove pogrešaka, kao u bloku koda ispod.
- U tražilici upitom
site:na Vašu domenu tražiti pojmove poput „Fatal error“ ili „Warning“. - Provjeriti upisuje li se u zapisnik pogrešaka i da se ne nalazi u web-korijenu.
# Pretražiti isporučene stranice na tekstove PHP pogrešaka (samo vlastite domene)
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
# Upisuje li se u zapisnik pogrešaka?
ls -l /var/log/php/
Skrivanje pogrešaka nije zamjena za njihovo otklanjanje. Zapisnik pun upozorenja znak je potrebe za održavanjem, a nakon provale često je važan trag. Što pritom zapisivati, a što ne, opisuje članak Sigurno zapisivati: bilježiti pogreške bez curenja podataka.
Zaključak: tiho u pregledniku, govorljivo u zapisniku
Pravilo za produkcijske poslužitelje je jednostavno: pogreške pripadaju u zapisnik, a ne na web-stranicu. U praksi ga ometaju zadane vrijednosti koje nisu zamišljene za produkciju, debug sklopke u aplikacijama i konfiguracije koje se nakon ažuriranja ili selidbi vrate unatrag.
Tko isključi display_errors i display_startup_errors, zaključa vrijednosti u poolu, provjeri debug način aplikacije i redovito provjerava rezultat, zatvara jedan od najčešćih izvora nepotrebnog odavanja informacija. Trud je malen, a učinak traje dok provjera ostaje dio svakog ažuriranja.
Jednu točku ne treba previdjeti: zapisnik pogrešaka nije kanta za smeće. Tko ga redovito čita, prepoznaje pogrešna ažuriranja, zastarjele funkcije, a ponekad i prve tragove napada, dugo prije nego što kupci išta primijete.
Kako PHP SOS konkretno pomaže?
- Provjeriti: očitavamo djelotvorne postavke pogrešaka po web-stranici i poolu te pretražujemo isporučene stranice na ispise pogrešaka.
- Prebaciti: PHP i postavke aplikacije postavljamo za produkcijski rad i zaključavamo ih od nadjačavanja.
- Postaviti zapisnike: zapisnici pogrešaka nalaze se izvan web-korijena, rotiraju se i dostupni su Vašim programerima.
- Zadržati trajno: u sigurnosnom ugovoru o održavanju postavke ponovno provjeravamo nakon svakog ažuriranja.
Česta pitanja
Nije li display_errors u PHP-u ionako isključen?
Nije automatski. Prema PHP priručniku ugrađena zadana vrijednost display_errors je 1. Tek isporučeni predložak php.ini-production postavlja Off. Koristi li Vaš poslužitelj taj predložak, ovisi o distribuciji, panelu ili image-u kontejnera i treba provjeriti.
Je li display_errors = Off dovoljan ako WordPress ili Laravel rade u debug načinu?
Nije. Mnogi CMS-ovi i frameworki donose vlastite stranice s pogreškama koje neovisno o PHP-u mogu prikazivati pojedinosti. Zato isključite i debug način aplikacije, primjerice WP_DEBUG_DISPLAY kod WordPressa ili APP_DEBUG kod Laravela i Symfonyja.
Kamo idu pogreške kad se više ne prikazuju?
Uz log_errors = On PHP ih upisuje u datoteku koju navodi error_log ili u zapisnik dotičnog načina rada, primjerice zapisnik pogrešaka Apachea ili PHP-FPM-a. Datoteka bi trebala biti izvan web-korijena i redovito se rotirati.
Kakve veze fatal_error_backtraces ima s PHP2Shellom?
Postavka postoji od PHP-a 8.5 i zadano je aktivna. php.net navodi fatal_error_backtraces=0 s naknadnim ponovnim pokretanjem kao jednu od prijelaznih mjera za PHP 8.5 dok ažuriranje na 8.5.12 još nije provedeno. Ne zamjenjuje ažuriranje.