Na kratko: Na živi spletni strani display_errors = Off sodi v php.ini, skupaj z display_startup_errors = Off, log_errors = On in dnevnikom napak zunaj spletnega korena. Dodatno morata CMS in ogrodja teči v produkcijskem načinu, sicer podrobnosti kažejo njihove lastne strani z napakami. Zaklenjene s php_admin_flag in po vsaki posodobitvi preverjene napake ostanejo v dnevniku, ne v brskalniku.
Kaj razkrije eno samo sporočilo o napaki
Opozorilo na vrhu strani, »Fatal error« namesto pregleda naročila: taki izpisi so videti nenevarni. Za napadalca so zemljevid. Pokažejo popolne poti do datotek, imena zbirk podatkov in tabel, uporabljene knjižnice in pogosto različico PHP.
Prav v razmerah, kot je PHP2Shell, v katerih se množično išče ranljive strežnike, naj strežnik o sebi razkrije čim manj. To ne zapre ranljivosti v interpreterju PHP, otežuje pa vsak nadaljnji korak po vdoru in naredi ciljne napade na vašo aplikacijo bolj mučne.
Dodaten vidik je varstvo podatkov. Sporočila o napakah lahko vsebujejo dele zahtev, na primer e-poštne naslove iz obrazcev. Kar se pojavi v brskalniku obiskovalca, ni več pod vašim nadzorom.
Privzete vrednosti niso namenjene produkciji
Številni predpostavljajo, da PHP napake sam skrije. Priročnik PHP pravi drugače: vgrajena privzeta vrednost display_errors je 1, display_startup_errors pa je od PHP 8.0 prav tako privzeto vklopljen. Šele priložena predloga php.ini-production oboje nastavi na Off. Priročnik display_errors izrecno imenuje razvojno funkcijo, ki je na sistemih v internetu nikoli ne bi smeli uporabljati.
Ali vaš strežnik uporablja produkcijsko predlogo, ni samoumevno. Slike vsebnikov se včasih zaženejo povsem brez php.ini, nadzorne plošče gostovanja nastavljajo lastne vrednosti, po selitvi pa se rado skopira razvojna predloga.
display_errors off: pregled nastavitev
Naslednje vrednosti so smiselne za produkcijske strežnike. Konstante, kot je E_ALL, delujejo samo v php.ini; v konfiguraciji Apache po priročniku PHP nimajo pomena in jih je treba podati kot število.
| Nastavitev | Produkcija | Namen |
|---|---|---|
display_errors |
Off |
brez napak v brskalniku |
display_startup_errors |
Off |
brez napak ob zagonu PHP v izpisu |
log_errors |
On |
napake zapisati v dnevnik |
error_log |
pot zunaj spletnega korena | lastna datoteka dnevnika za vsako spletno stran ali pool |
error_reporting |
E_ALL |
beležiti vse napake, ne prikazovati |
html_errors |
Off |
navadno besedilo namesto HTML v dnevnikih |
zend.exception_ignore_args |
On |
brez argumentov funkcij v sledeh sklada |
fatal_error_backtraces |
0 do posodobitve (samo PHP 8.5) |
prehodni ukrep po php.net |
zend.exception_ignore_args je še posebej dragocen. Brez te nastavitve lahko sledi sklada v dnevnikih in na straneh z napakami vsebujejo argumente klicev funkcij, torej tudi gesla ali žetone, ki so se pravkar obdelovali.
; php.ini spletnega 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
; samo PHP 8.5, prehodni ukrep do posodobitve na 8.5.12:
; fatal_error_backtraces = 0
V poolu FPM najpomembnejše vrednosti dodatno zaklenete, da jih nobena aplikacija z ini_set() ne vklopi znova:
; 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
Nato znova zaženite zadevno storitev PHP. Kako zaklepanje natančno deluje, pojasnjuje prispevek php_admin_value: zaklepanje nastavitev pred prepisovanjem. Popolno osnovno konfiguracijo najdete pod Varna konfiguracija php.ini.
Drugo stikalo: način odpravljanja napak v aplikaciji
PHP je samo polovica zgodbe. Sodobna ogrodja napake sama ujamejo in prikažejo lastne, pogosto zelo obsežne strani z napakami. V načinu odpravljanja napak vsebujejo izseke izvorne kode, konfiguracijske vrednosti in včasih okoljske spremenljivke. Te strani se pojavijo tudi, če je display_errors v PHP izklopljen.
Zato za vsako aplikacijo preverite njeno lastno stikalo. Pri WordPress sta to WP_DEBUG in WP_DEBUG_DISPLAY v wp-config.php, pri Laravel in Symfony APP_DEBUG in okolje v datoteki .env, pri drugih sistemih produkcijski način po dokumentaciji proizvajalca. Na živem sistemu mora biti način odpravljanja napak trajno izklopljen, tudi »samo za kratek preizkus« je tveganje.
Kako se napake v aplikaciji čisto obravnavajo, namesto da bi jih zatirali, opisuje Napake in izjeme v PHP.
Tako najdete izpise napak, preden jih najdejo drugi
Enkratna nastavitev ne zadošča. Izpisi napak se po posodobitvah, selitvah ali spremembah nadzorne plošče vrnejo. Pomaga kratek postopek preverjanja:
- Preverite veljavne vrednosti v spletnem kontekstu, ne samo v ukazni vrstici, na primer s kratko položeno in nato takoj izbrisano preverjalno datoteko.
- Preverite način odpravljanja napak vsake aplikacije v njeni konfiguracijski datoteki.
- Dostavljene strani preiščite za tipična besedila napak, kot v bloku kode spodaj.
- V iskalniku s poizvedbo
site:po svoji domeni iščite izraze, kot sta »Fatal error« ali »Warning«. - Preverite, ali se v dnevnik napak piše in ali ne leži v spletnem korenu.
# Preiščite dostavljene strani za besedila napak PHP (samo lastne 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
# Ali se v dnevnik napak piše?
ls -l /var/log/php/
Skrivanje napak ni nadomestilo za njihovo odpravljanje. Dnevnik, poln opozoril, je namig na potrebo po vzdrževanju, po vdoru pa je pogosto pomembna sled. Kaj pri tem beležite in česa ne, opisuje Varno beleženje: beležiti napake brez uhajanja podatkov.
Sklep: tiho v brskalniku, zgovorno v dnevniku
Pravilo za produkcijske strežnike je preprosto: napake sodijo v dnevnik, ne na spletno stran. V praksi spodleti zaradi privzetih vrednosti, ki niso namenjene produkciji, zaradi stikal za odpravljanje napak v aplikacijah in zaradi konfiguracij, ki po posodobitvah ali selitvah zdrsnejo nazaj.
Kdor izklopi display_errors in display_startup_errors, vrednosti v poolu zaklene, nadzira način odpravljanja napak aplikacije in rezultat redno preverja, zapre enega najpogostejših virov nepotrebnega razkrivanja informacij. Napor je majhen, učinek traja, dokler preverjanje ostaja del vsake posodobitve.
Ena točka naj ne utone: dnevnik napak ni smetnjak. Kdor ga redno bere, prepozna napačne posodobitve, zastarele funkcije in včasih tudi prve sledi napada, dolgo preden stranke karkoli opazijo.
Kako PHP SOS pomaga konkretno?
- Preverjanje: veljavne nastavitve napak preberemo za vsako spletno stran in pool ter preiščemo dostavljene strani za izpise napak.
- Preklop: nastavitve PHP in aplikacij nastavimo za produkcijsko delovanje in jih zaklenemo pred prepisovanjem.
- Vzpostavitev dnevnikov: dnevniki napak ležijo zunaj spletnega korena, se rotirajo in so dostopni vašim razvijalcem.
- Trajno vzdrževanje: v okviru varnostne vzdrževalne pogodbe nastavitve po vsaki posodobitvi znova preverimo.
Pogosta vprašanja
Ali display_errors v PHP ni tako ali tako izklopljen?
Ne samodejno. Po priročniku PHP je vgrajena privzeta vrednost display_errors enaka 1. Šele priložena predloga php.ini-production nastavi Off. Ali vaš strežnik to predlogo uporablja, je odvisno od distribucije, nadzorne plošče ali slike vsebnika in naj se preveri.
Ali zadošča display_errors = Off, če WordPress ali Laravel tečeta v načinu odpravljanja napak?
Ne. Številni CMS in ogrodja prinašajo lastne strani z napakami, ki lahko neodvisno od PHP prikažejo podrobnosti. Zato izklopite tudi način odpravljanja napak aplikacije, na primer WP_DEBUG_DISPLAY pri WordPress ali APP_DEBUG pri Laravel in Symfony.
Kam gredo napake, če se ne prikazujejo več?
Z log_errors = On jih PHP zapiše v datoteko, ki jo določa error_log, ali v dnevnik zadevnega načina delovanja, na primer dnevnik napak Apache ali PHP-FPM. Datoteka naj leži zunaj spletnega korena in se redno rotira.
Kaj ima fatal_error_backtraces opraviti s PHP2Shell?
Nastavitev obstaja od PHP 8.5 in je privzeto aktivna. php.net fatal_error_backtraces=0 s sledečim ponovnim zagonom navaja kot enega od prehodnih ukrepov za PHP 8.5, dokler posodobitev na 8.5.12 še ni izvedena. Posodobitve ne nadomešča.