2009-12-16 2 views
24

Saluti, Spero di rendere sicuro il mio piccolo programma in modo che i potenziali utenti malintenzionati non possano visualizzare i file riservati sul server.Sanitize percorso file in PHP

$path = "/home/gsmcms/public_html/central/app/webroot/{$_GET['file']}"; 


    if(file_exists($path)) { 
     echo file_get_contents($path); 
    } else { 
     header('HTTP/1.1 404 Not Found'); 
    } 

Fuori della parte superiore della mia testa so che di input come ad esempio '../../../../../../etc/passwd' sarebbe guai, ma chiedendosi quali altre input maliziosi che dovrei aspettarmi e come prevenirli.

+1

Basta escludere qualsiasi input contenente ../ – halfdan

+2

D'accordo che avrebbe risolto un problema, ma sto supponendo che ci sono molti altri pericoli ho bisogno di guardare fuori per. Sto cercando una buona soluzione rivestita di ferro per tutti loro – SeanDowney

+3

@halfdan - cerca sempre di evitare un approccio black-list alla sicurezza come questa, ci sarà sempre qualcosa che ti manca. Come l'uso di caratteri backspace, tabulazioni, newline, caratteri null, altri caratteri Unicode o char unicode intenzionalmente interrotti che passerebbero il filtro, ma causano comunque la funzione PHP di fare qualcosa che stavi cercando di proteggerlo. Metti alla prova quello che vuoi veramente: che il percorso risultante è in una posizione sicura. – Cheekysoft

risposta

33

realpath() consente di convertire qualsiasi percorso che può contenere informazioni relative in un percorso assoluto ... è quindi possibile garantire che il percorso si trovi in ​​una determinata sottodirectory da cui si desidera consentire il download.

+4

Questa era la mia soluzione finale: $ baseDir = "/ home/gsmcms/public_html/centrale/app/webroot/"; $ percorso = realpath ($ baseDir. $ _GET ['file']); // se baseDir non è in primo piano 0 == strpos, molto probabilmente tentativo di hacking if (strpos ($ path, $ baseDir)) { \t die ('Percorso non valido'); } elseif (file_exists (percorso $)) { \t echo file_get_contents ($ path); } else { \t intestazione ('HTTP/1.1 404 non trovato '); \t echo "Impossibile trovare il file richiesto"; } – SeanDowney

+1

@SeanDowney c'è un bug nella soluzione, ovvero dovresti controllare che la stringa non restituisca falso o diverso da zero, cosa che non stai facendo. – Michael

+1

realpath() è un buon amico ma non abbastanza - restituisce solo il percorso completo se ci si riferisce a un percorso esistente. Altrimenti restituirebbe falso; in alcuni casi ciò non causerà alcun problema, ma considerando un'operazione "salva come" con la creazione automatica della sottodirectory, può rovinare il gioco. (L'utente ti dice dove mettere il file ma tu rispondi "percorso non valido" poiché realpath restituisce false.) Probabilmente non è quello che stai facendo; ho solo pensato di menzionarlo – dkellner

11

Utilizzare basename anziché tentare di anticipare tutti i percorsi non sicuri che un utente può fornire.

+0

questo può funzionare in alcune situazioni, tuttavia mi aspetto che l'input includa anche le directory, es: '/js/jquery/jquery.js' – SeanDowney

6

Se è possibile, utilizzare una lista di come una matrice di file consentiti e controllare l'input in base a ciò: se il file richiesto dall'utente non è presente nell'elenco, rifiutare la richiesta.

+0

Questa sarebbe l'idea migliore, ma probabilmente più lavoro di quello che voglio fare :) – SeanDowney

+0

A meno che tu non voglia perdere la fonte di tutti i tuoi file sotto il tuo webroot, probabilmente vorrai farlo. – Cheekysoft

4

C'è un ulteriore e significativo rischio per la sicurezza qui. Questo script inietterà l'origine di un file nello stream di output senza alcuna elaborazione sul lato server. Ciò significa che tutto il codice sorgente di qualsiasi file accessibile verrà divulgato su Internet.

+0

buon punto, aggiungerò una whitelist di estensioni consentite come: js, css, jpg, gif ... – SeanDowney

7

Soluzione dal PO:

$baseDir = "/home/gsmcms/public_html/central/app/webroot/"; 
$path = realpath($baseDir . $_GET['file']); 

// if baseDir isn't at the front 0==strpos, most likely hacking attempt 
if(strpos($path, $baseDir) !== 0 || strpos($path, $baseDir) === false) { 
    die('Invalid Path'); 
} elseif(file_exists($path)) { 
    echo file_get_contents($path); 
} else { 
    header('HTTP/1.1 404 Not Found'); 
    echo "The requested file could not be found"; 
} 
+0

impara ad usare le funzionalità avanzate di SO! :) semplifica * copia-incolla * –

+0

FYI, ho trovato un bug in questa soluzione, ma la mia modifica contenente la correzione è stata respinta. – Michael

+0

Si prega di controllare quanto sopra. Dovrebbe essere più esplicito e giustificato a fini di sanità. –

1

Anche se si sta utilizzando realpath, si dovrebbe comunque togliere tutto ".." prima di utilizzarlo. In caso contrario, un utente malintenzionato può leggere l'intera struttura di directory del server con forza bruta, ad es. "valid_folder /../../ test_if_this_folder_name_exists/valid_folder" - se l'applicazione accetta questo percorso, l'utente malintenzionato sa che la cartella esiste.

0

Per rimuovere tutto il /. /.. o \. \ .. e convertire tutte le barre in avanti perché i diversi ambienti accettano la barra diretta. Questo dovrebbe fornire un filtro abbastanza sicuro per l'input del percorso. Nel tuo codice dovresti confrontarlo con le directory madri a cui non vuoi accedere per ogni evenienza.

$path = realpath(implode('/', array_map(function($value) {return trim($value, '.');}, explode('/', str_replace('\\', '/', $path))))); 
+0

@Koby: Perché hai modificato la mia risposta? Lo str_replace mette tutte le barre in avanti e il realpath fa tutto in un'unica barra .. non modificare il mio codice –