2009-12-16 11 views
24

Pozdrowienia, Mam nadzieję, że mój drobny program będzie bezpieczny, tak aby potencjalni szkodliwi użytkownicy nie mogli wyświetlać poufnych plików na serwerze.Ścieżka do pliku sanitize w 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'); 
    } 

Off górze mojej głowie Wiem, że wejście takie jak „../../../../../../etc/passwd” byłoby kłopotów, ale zastanawiasz się, jakie inne podejrzane dane, których powinienem się spodziewać i jak im zapobiegać.

+1

Wystarczy wykluczyć żadnych danych zawierającą ../ – halfdan

+2

zgodzili się, że mogłoby rozwiązać jeden problem, ale jestem przy założeniu, że istnieje wiele innych zagrożeń muszę zwrócić uwagę. Szukam dobrego żelaznego rozwiązania dla wszystkich z nich – SeanDowney

+3

@halfdan - zawsze staraj się unikać podejścia opartego na czarnej liście do takiego bezpieczeństwa, zawsze będzie coś, za czym tęsknisz. Takich jak użycie znaków backspace, tabulatorów, znaków nowej linii, znaków zerowych, innych znaków Unicode lub celowo przerwanych znaków Unicode, które przesłałyby twój filtr, ale nadal powodują, że funkcja PHP robi coś, z czym próbujesz ją chronić. Sprawdź, czego naprawdę chcesz: wynikowa ścieżka znajduje się w bezpiecznym miejscu. – Cheekysoft

Odpowiedz

33

realpath() pozwoli ci przekonwertować dowolną ścieżkę, która może zawierać informacje względne na ścieżkę bezwzględną ... możesz wtedy upewnić się, że ścieżka znajduje się pod określonym podkatalogiem, z którego chcesz pobierać pliki.

+4

To było moje ostateczne rozwiązanie: $ baseDir = "/ home/gsmcms/public_html/central/app/webroot/"; $ path = realpath ($ baseDir. $ _GET ['plik']); // jeśli baseDir nie znajduje się z przodu 0 == strpos, najprawdopodobniej próba hakowania if (strpos ($ path, $ baseDir)) { \t die ('Invalid Path'); } elseif (file_exists ($ path)) { \t echo file_get_contents ($ ścieżka); } else { \t nagłówek ("HTTP/1.1 404 Not Found "); \t echo "Nie można znaleźć żądanego pliku"; } – SeanDowney

+1

@SeanDowney W twoim rozwiązaniu jest błąd, a mianowicie sprawdź, czy strpos nie zwraca wartości false lub niezerowej, których nie robisz. – Michael

+1

realpath() jest dobrym przyjacielem, ale niewystarczającym - zwraca tylko pełną ścieżkę, jeśli odwołujesz się do istniejącej ścieżki. W przeciwnym razie zwróciłoby fałsz; w niektórych przypadkach nie spowoduje to żadnych problemów, ale biorąc pod uwagę operację "zapisz jako" z automatycznym tworzeniem podkatalogu, może zepsuć grę. (Użytkownik informuje, gdzie umieścić plik, ale odpowiada "nieprawidłową ścieżką", ponieważ funkcja realpath zwraca wartość false.) Prawdopodobnie nie jest to co robisz; tylko pomyślałem o tym wspomnieć. – dkellner

11

Należy używać basename zamiast próbować przewidzieć wszystkie niebezpieczne ścieżki, które może podać użytkownik.

+0

może to działać w niektórych sytuacjach, ale spodziewam się, że dane wejściowe będą zawierać również katalogi, np. "/js/jquery/jquery.js" – SeanDowney

6

Jeśli możesz, użyj białej listy , takiej jak tablica dozwolonych plików i sprawdź dane wejściowe: jeśli plik żądany przez użytkownika nie występuje na tej liście, odmów żądanie.

+0

To byłby najlepszy pomysł, ale prawdopodobnie więcej pracy niż chcę zrobić :) – SeanDowney

+0

Jeśli nie chcesz przeciekać źródła wszystkich plików w swoim webroot, prawdopodobnie zechcesz to zrobić. – Cheekysoft

4

Istnieje dodatkowe i istotne zagrożenie bezpieczeństwa tutaj. Skrypt ten wstrzyknie źródło pliku do strumienia wyjściowego bez przetwarzania po stronie serwera. Oznacza to, że cały kod źródłowy wszystkich dostępnych plików zostanie przesłany do Internetu.

+0

Dobra sprawa, dodam białą listę dozwolonych rozszerzeń, takich jak: js, css, jpg, gif ... – SeanDowney

7

Rozwiązanie przez OP:

$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

proszę nauczyć się korzystać z zaawansowanych funkcji SO! :) Upraszcza * kopiuj-wklej * –

+0

FYI, znalazłem błąd w tym rozwiązaniu, ale moja edycja zawierająca poprawkę została odrzucona. – Michael

+0

Proszę sprawdzić powyższe. Powinien być bardziej wyraźny i uzasadniony z punktu widzenia zdrowia psychicznego. –

1

Nawet jeśli używasz realpath, należy jeszcze rozebrać wszystko ".." przed użyciem. W przeciwnym razie atakujący może odczytać całą strukturę katalogów serwerów za pomocą brutalnej siły, np. "valid_folder /../../ test_if_this_folder_nameists/valid_folder" - jeśli aplikacja akceptuje tę ścieżkę, atakujący wie, że folder istnieje.

0

Aby usunąć wszystkie /. /.. lub \. \ .. i konwertuj na cały ukośnik, ponieważ różne środowiska zaakceptują ukośnik. Powinno to zapewnić dość bezpieczny filtr do wprowadzania ścieżek. W swoim kodzie powinieneś porównywać go do katalogów nadrzędnych, na które nie chcesz mieć dostępu na wszelki wypadek.

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

@Koby: Dlaczego edytujesz moją odpowiedź? Str_replace umieszcza wszystkie przednie ukośniki, a realpath sprawia, że ​​jest to pojedynczy pojedynczy slash ... nie edytuj mojego kodu –