L'opzione PHP 'cgi.fix_pathinfo' è davvero pericolosa con Nginx + PHP-FPM?


51

C'è stato un sacco di parlare di un problema di sicurezza relativo alla cgi.fix_pathinfopossibilità di PHP usato con Nginx (di solito PHP-FPM, CGI veloce).

Di conseguenza, il file di configurazione nginx predefinito usato diceva:

# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

Tuttavia, ora, il wiki "ufficiale" di Nginx afferma che PATH_INFO può essere gestito correttamente senza disabilitare l'opzione PHP sopra. E allora?

Domande

  • Puoi spiegare chiaramente cosa fa cgi.fix_pathinfo? (il documento ufficiale dice semplicemente : "Per ulteriori informazioni su PATH_INFO, consultare le specifiche CGI")
  • Cosa faranno veramente PHP con queste PATH_INFOe SCRIPT_FILENAMEvariabili?
  • Perché e come può essere pericoloso con Nginx? ( esempi dettagliati )
  • Il problema persiste nelle ultime versioni di questi programmi?
  • Apache è vulnerabile?

Sto cercando di capire il problema ad ogni passaggio. Ad esempio, non capisco perché l'uso del socket Unix php-fpm possa evitare questo problema.


1
Potresti rispondere alla tua domanda comprendendo la differenza tra PATH_INFO e PATH_TRANSLATED: blogs.msdn.com/b/david.wang/archive/2005/08/04/…
Giovanni Tirloni,

Risposte:


79

TL; DR - la correzione (che potrebbe non essere nemmeno necessaria) è MOLTO SEMPLICE e alla fine di questa risposta.

Cercherò di rispondere alle tue domande specifiche, ma il tuo malinteso su cosa sia PATH_INFO rende le domande stesse un po 'sbagliate.

  • La prima domanda dovrebbe essere "Cos'è questo business delle informazioni sul percorso?"

  • La tua prossima domanda avrebbe dovuto essere: "In che modo PHP determina cosa PATH_INFOe cosa SCRIPT_FILENAMEsono?"

    • Le versioni precedenti di PHP erano ingenui e tecnicamente non supportavano nemmeno PATH_INFO, quindi ciò che doveva essere è PATH_INFOstato munguto sul SCRIPT_FILENAMEquale, sì, è rotto in molti casi. Non ho una versione abbastanza vecchia di PHP con cui testare, ma credo che abbia visto SCRIPT_FILENAMEl'intero shebang: "/path/to/script.php/THIS/IS/PATH/INFO" nell'esempio sopra (preceduto da il docroot come al solito).
    • Con cgi.fix_pathinfo abilitato, PHP ora trova correttamente "/ THIS / IS / PATH / INFO" per l'esempio sopra e lo inserisce PATH_INFOe SCRIPT_FILENAMEottiene solo la parte che punta allo script richiesto (con il prefisso ovviamente con il docroot).
    • Nota: quando PHP ha iniziato a supportare effettivamente PATH_INFO, ha dovuto aggiungere un'impostazione di configurazione per la nuova funzionalità in modo che le persone che eseguono script che dipendevano dal vecchio comportamento potessero eseguire nuove versioni di PHP. Ecco perché c'è anche un interruttore di configurazione per questo. Avrebbe dovuto essere integrato (con il comportamento "pericoloso") dall'inizio.
  • Ma come fa PHP a sapere quale parte è lo script e quali informazioni sul percorso? E se l'URI fosse simile a:

    http://example.com/path/to/script.php/THIS/IS/PATH/INFO.php?q=foo

    • Questa può essere una domanda complessa in alcuni ambienti. Quello che succede in PHP è che trova la prima parte del percorso URI che non corrisponde a nulla nella documentazione del server. Per questo esempio, vede che sul tuo server non hai "/docroot/path/to/script.php/THIS" ma sicuramente hai "/docroot/path/to/script.php" quindi ora il SCRIPT_FILENAMEè stato determinato e PATH_INFOottiene il resto.
    • Quindi ora il buon esempio del pericolo che è ben dettagliato nei documenti di Nginx e nella risposta di Hrvoje Špoljar (non si può essere pignoli su un esempio così chiaro) diventa ancora più chiaro: dato l'esempio di Hrvoje ("esempio http: //. com / foo.jpg / nonexistent.php "), PHP vede un file nel tuo docroot" /foo.jpg "ma non vede nulla chiamato" /foo.jpg/nonexistent.php "quindi SCRIPT_FILENAMEottiene" /foo.jpg " (di nuovo, con il prefisso docroot) e PATH_INFOottiene "/nonexistent.php".
  • Perché e come può essere pericoloso ora dovrebbe essere chiaro:

    • Il web server non è davvero in errore - sta semplicemente inviando l'URI a PHP, il che trova innocentemente che "foo.jpg" in realtà contiene contenuti PHP, quindi lo esegue (ora sei stato promosso!). Questo NON è particolare per Nginx in sé.
  • Il VERO problema è che si lascia che il contenuto non attendibile essere caricato da qualche parte senza sanificazione e consentire altre richieste arbitrarie nella stessa posizione, che PHP esegue felicemente quando può.
  • Nginx e Apache potrebbero essere compilati o configurati per prevenire richieste usando questo trucco, e ci sono molti esempi su come farlo, inclusa la risposta di user2372674 . Questo articolo del blog spiega bene il problema, ma manca la soluzione giusta.

  • Tuttavia, la soluzione migliore è assicurarsi che PHP-FPM sia configurato correttamente in modo che non eseguirà mai un file a meno che non termini con ".php". Vale la pena notare che le versioni recenti di PHP-FPM (~ 5.3.9 +?) Hanno questo come predefinito, quindi questo pericolo non è più un problema.

La soluzione

Se hai una versione recente di PHP-FPM (~ 5.3.9 +?), Non devi fare nulla, poiché il comportamento sicuro di seguito è già predefinito.

Altrimenti, trova il www.conffile php-fpm (forse /etc/php-fpm.d/www.confdipende dal tuo sistema). Assicurati di avere questo:

security.limit_extensions = .php

Ancora una volta, questo è predefinito in molti luoghi in questi giorni.

Nota che ciò non impedisce a un utente malintenzionato di caricare un file ".php" in una cartella di upload di WordPress e di eseguirlo utilizzando la stessa tecnica. Hai ancora bisogno di avere una buona sicurezza per le tue applicazioni.


5
Bella risposta! Per chiarire: se, come dici tu, PHP determina cos'è SCRIPT_FILENAME, perché c'è una fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;linea nel mio nginxconf? Sovrascrive gli sforzi di PHP per scoprire il valore di SCRIPT_FILENAMEper sé?
Totor

Esiste una funzione per ottenere il valore di security.limit_extensions? Ci ho provato phpinfo(), ini_get(security.limit_extensions)e ini_get_all()senza successo.
elbowlobstercowstand,

Grazie, se le versioni recenti di PHP-FPM (~ 5.3.9 +?) Hanno questo come predefinito, perché php7.1 ne ha bisogno? O questo articolo è sbagliato?
Yevgeniy Afanasyev,

14

In sostanza senza questo è possibile caricare file con codice php chiamato 'foo.jpg' sul web server; quindi richiederlo come http: //domain.tld/foo.jpg/nonexistent.php e lo stack del server web dirà erroneamente oh; questo è un PHP; Ho bisogno di elaborare questo, non riuscirà a trovare foo.jpg / inesistente.php quindi tornerà a foo.jpg ed elaborerà foo.jpg come codice php. Ciò è pericoloso poiché apre il sistema a un'intrusione molto semplice; qualsiasi applicazione web che consente il caricamento di immagini, ad esempio, diventa uno strumento per caricare backdoor.

Per quanto riguarda l'utilizzo di php-fpm con socket unix per evitarlo; IMO non risolverà il problema.


Ripeti solo ciò che può essere letto sui link che ho fornito. Non spieghi il vero meccanismo. La tua risposta richiede un valore aggiunto IMHO.
Totor,

6
Questo può essere vero, ma il tuo titolo ha domande e la risposta a questa domanda è nella mia risposta. Se lo vuoi esplicitamente; si è pericoloso; molto pericoloso.
Hrvoje Špoljar,

1 / La mia risposta non si limita al suo titolo: ha un corpo. 2 / user109322 ha dimostrato che hai torto: qualunque valore utilizzato noncgi.fix_pathinfo è pericoloso, perché la configurazione predefinita è sicura (eseguirà solo i file con l' estensione). php-fpm.php
Totor

2

Nel wiki di Nginx come misura di sicurezza

if (!-f $document_root$fastcgi_script_name) {
    return 404;
}

è incluso nel blocco posizione. In altri tutorial

try_files $uri =404;

viene usato, che dovrebbe fare lo stesso, ma può dare problemi secondo il wiki di Nginx. Con queste opzioni, cgi.fix_pathinfo=1non dovrebbe più essere un problema. Ulteriori informazioni possono essere trovate qui .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.