In breve: su un sito live display_errors = Off va in php.ini, insieme a display_startup_errors = Off, log_errors = On e a un log degli errori fuori dal webroot. Inoltre CMS e framework devono girare in modalità produzione, altrimenti le loro pagine di errore mostrano i dettagli. Con il blocco tramite php_admin_flag e la verifica dopo ogni aggiornamento, gli errori restano nel log invece che nel browser.
Che cosa rivela un singolo messaggio di errore
Un avviso al margine superiore della pagina, un «Fatal error» al posto del riepilogo dell'ordine: output simili sembrano innocui. Per un aggressore sono una mappa. Mostrano percorsi di file completi, nomi di database e tabelle, librerie in uso e spesso la versione di PHP.
Proprio in situazioni come PHP2Shell, in cui si cercano in massa server vulnerabili, un server dovrebbe rivelare di sé il meno possibile. Questo non chiude alcuna falla nell'interprete PHP, ma rende più difficile ogni passo successivo a un'intrusione e rende più laboriosi gli attacchi mirati alla Sua applicazione.
Si aggiunge la protezione dei dati. I messaggi di errore possono contenere parti di richieste, ad esempio indirizzi e-mail dei moduli. Ciò che compare nel browser di un visitatore non è più sotto il Suo controllo.
I valori predefiniti non sono pensati per la produzione
Molti partono dal presupposto che PHP nasconda gli errori da sé. Il manuale PHP dice altro: il valore predefinito integrato di display_errors è 1, e anche display_startup_errors è attivo per impostazione predefinita da PHP 8.0. Solo il modello fornito php.ini-production imposta entrambi su Off. Il manuale definisce espressamente display_errors una funzione di sviluppo che non dovrebbe mai essere usata su sistemi connessi a Internet.
Che il Suo server usi il modello di produzione non è scontato. Le immagini container a volte partono addirittura senza php.ini, i pannelli di hosting impostano valori propri e dopo un trasloco si copia volentieri il modello di sviluppo.
display_errors off: le impostazioni in sintesi
I valori seguenti sono sensati per i server di produzione. Le costanti come E_ALL funzionano solo in php.ini; nella configurazione di Apache, secondo il manuale PHP, non hanno significato e vanno indicate come numero.
| Impostazione | Produzione | Scopo |
|---|---|---|
display_errors |
Off |
nessun errore nel browser |
display_startup_errors |
Off |
nessun errore all'avvio di PHP nell'output |
log_errors |
On |
scrivere gli errori nel registro |
error_log |
percorso fuori dal webroot | file di log proprio per sito o pool |
error_reporting |
E_ALL |
registrare tutti gli errori, non visualizzarli |
html_errors |
Off |
testo semplice invece di HTML nei registri |
zend.exception_ignore_args |
On |
nessun argomento di funzione negli stack trace |
fatal_error_backtraces |
0 fino all'aggiornamento (solo PHP 8.5) |
misura transitoria secondo php.net |
zend.exception_ignore_args è particolarmente prezioso. Senza questa impostazione, gli stack trace in log e pagine di errore possono contenere gli argomenti delle chiamate di funzione, quindi anche password o token appena elaborati.
; php.ini della SAPI web
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
; solo PHP 8.5, misura transitoria fino all'aggiornamento a 8.5.12:
; fatal_error_backtraces = 0
Nel pool FPM blocchi inoltre i valori più importanti, in modo che nessuna applicazione li riattivi con ini_set():
; ad es. /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
Poi riavvii il rispettivo servizio PHP. Come funziona esattamente il blocco lo spiega l'articolo php_admin_value: bloccare le impostazioni contro la sovrascrittura. Una configurazione di base completa si trova in Configurare php.ini in modo sicuro.
Il secondo interruttore: la modalità debug dell'applicazione
PHP è solo metà dell'opera. I framework moderni intercettano gli errori da soli e mostrano pagine di errore proprie, spesso molto dettagliate. In modalità debug contengono estratti del codice sorgente, valori di configurazione e talvolta variabili d'ambiente. Queste pagine compaiono anche quando display_errors in PHP è disattivato.
Verifichi quindi per ogni applicazione l'interruttore proprio. In WordPress sono WP_DEBUG e WP_DEBUG_DISPLAY in wp-config.php, in Laravel e Symfony APP_DEBUG e l'ambiente nel file .env, in altri sistemi la modalità produzione secondo la documentazione del produttore. Su un sistema live la modalità debug deve restare sempre disattivata, anche «solo per un attimo, per provare» è un rischio.
Come gestire in modo pulito gli errori nell'applicazione, invece di sopprimerli, lo descrive Errori ed eccezioni in PHP.
Come trovare gli output di errore prima che lo facciano altri
Impostare una volta non basta. Gli output di errore tornano dopo aggiornamenti, traslochi o modifiche del pannello. Una breve procedura di verifica aiuta:
- Verificare i valori effettivi nel contesto web, non solo da riga di comando, ad esempio con un file di prova collocato per poco e subito cancellato.
- Controllare la modalità debug di ogni applicazione nel suo file di configurazione.
- Scandire le pagine consegnate alla ricerca di testi di errore tipici, come nel blocco di codice qui sotto.
- Nel motore di ricerca, con una query
site:sul Suo dominio, cercare termini come «Fatal error» o «Warning». - Verificare che nel log degli errori si scriva e che non si trovi nel webroot.
# Scandire le pagine consegnate alla ricerca di testi di errore PHP (solo domini propri)
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
# Si sta scrivendo nel log degli errori?
ls -l /var/log/php/
Nascondere gli errori non sostituisce l'eliminarli. Un log pieno di avvisi indica un bisogno di manutenzione e, dopo un'intrusione, è spesso una traccia importante. Che cosa registrare in proposito e che cosa no lo descrive Registrare in modo sicuro: log degli errori senza fughe di dati.
Conclusione: silenzioso nel browser, loquace nel log
La regola per i server di produzione è semplice: gli errori vanno nel registro, non sul sito. Nella pratica fallisce per valori predefiniti non pensati per la produzione, per interruttori di debug nelle applicazioni e per configurazioni che tornano indietro dopo aggiornamenti o traslochi.
Chi disattiva display_errors e display_startup_errors, blocca i valori nel pool, controlla la modalità debug dell'applicazione e verifica regolarmente il risultato chiude una delle fonti più frequenti di divulgazione inutile di informazioni. Lo sforzo è modesto, l'effetto dura finché la verifica resta parte di ogni aggiornamento.
Un punto non dovrebbe andare perso: il log degli errori non è un cestino. Chi lo legge regolarmente riconosce aggiornamenti difettosi, funzioni obsolete e talvolta anche le prime tracce di un attacco, molto prima che i clienti si accorgano di qualcosa.
Come aiuta PHP SOS in concreto?
- Verificare: leggiamo le impostazioni di errore effettive per sito e pool e scandiamo le pagine consegnate alla ricerca di output di errore.
- Passare alla produzione: impostiamo le configurazioni di PHP e dell'applicazione per l'esercizio produttivo e le blocchiamo contro la sovrascrittura.
- Configurare i registri: i log degli errori si trovano fuori dal webroot, vengono ruotati e sono accessibili ai Suoi sviluppatori.
- Mantenere nel tempo: nel contratto di manutenzione della sicurezza verifichiamo di nuovo le impostazioni dopo ogni aggiornamento.
Domande frequenti
In PHP display_errors non è comunque disattivato?
Non automaticamente. Secondo il manuale PHP, il valore predefinito integrato di display_errors è 1. Solo il modello fornito php.ini-production imposta Off. Se il Suo server usi questo modello dipende da distribuzione, pannello o immagine container e andrebbe verificato.
display_errors = Off basta se WordPress o Laravel girano in modalità debug?
No. Molti CMS e framework hanno proprie pagine di errore che possono mostrare dettagli indipendentemente da PHP. Disattivi quindi anche la modalità debug dell'applicazione, ad esempio WP_DEBUG_DISPLAY in WordPress o APP_DEBUG in Laravel e Symfony.
Dove vanno gli errori se non vengono più visualizzati?
Con log_errors = On PHP li scrive nel file indicato da error_log o nel registro della rispettiva modalità di esercizio, ad esempio il log degli errori di Apache o PHP-FPM. Il file dovrebbe trovarsi fuori dal webroot ed essere ruotato regolarmente.
Che cosa c'entra fatal_error_backtraces con PHP2Shell?
L'impostazione esiste da PHP 8.5 ed è attiva per impostazione predefinita. php.net indica fatal_error_backtraces=0 con successivo riavvio come una delle misure transitorie per PHP 8.5, finché l'aggiornamento a 8.5.12 è ancora in sospeso. Non sostituisce l'aggiornamento.