Bezpieczeństwo dostępu do plików
Nieodzownym elementem każdej aplikacji internetowej są pliki tekstowe. Występują one jako miejsce przechowywania danych konfiguracyjnych, szablonów czy samego kodu PHP. Mimo prostoty korzystania z nich należy podjąć pewne kroki, w celu uczynienia tego procesu bezpieczniejszym. Poniżej znajduje się zestawienie kilku sposobów, które to umożliwiają.
Zadanie: dołączenie pliku przekazanego w parametrze
Opis: Dobrą praktyką w tworzeniu niewielkich stron jest utworzenie głównego pliku szablonu oraz dołączanie właściwej treści na podstawie nazwy pliku przekazanej w parametrze adresu URL.
Niebezpieczeństwo: Directory Traversal – listowanie dowolnego katalogu w systemie
Rozwiązania:
1. Filtracja nazwy pliku
a) Ograniczenie nazwy pliku do określonego zakresu znaków.
Korzystając z możliwości dawanych przez mod_rewrite możemy ograniczyć zestaw znaków składający się na nazwę pliku tylko do znaków alfanumerycznych. Dzięki temu do skryptu nie trafią dane umożliwiające napastnikowi wędrowanie między katalogami.
RewriteEngine on RewriteCond %{QUERY_STRING} file=[A-z0-9]+$ [NC] RewriteRule .* - [L] RewriteRule .* - [F,L]
b) Filtrowanie nazwy pliku.
Podobny efekt jak powyżej możemy uzyskać korzystając z PHPowej funkcji ctype_alnum. Umożliwia ona określenie czy podany ciąg znaków składa się tylko ze znaków alfanumerycznych. Jeśli chcielibyśmy rozszerzyć zakres znaków o dodatkowe elementy możemy posłużyć się funkcją preg_match tworząc odpowiednie wyrażenie regularne np.
if( !preg_match( '@^[A-z0-9]+$@', $_GET['file'] ) ) die( 'Nazwa pliku nie jest poprawna.' );
c) Wyciągnięcie nazwy pliku z parametru
Kolejną przydatną funkcją PHP jest basename. Służy ona do wyciągnięcia nazwy pliku z podanej ścieżki. Korzystając z niej mamy pewność, że nawet gdy napastnik zmodyfikuje parametr z nazwą pliku tak by umożliwiał on wędrówkę po katalogach nasz skrypt będzie bezpieczny.
2. Sprawdzenie czy plik istnieje.
Po weryfikacji poprawności nazwy pliku należy sprawdzić czy znajduje się on w katalogu z plikami do dołączenia. Przyda się to w wypadku gdy ktoś zmieni ustawione przez nas wartości zmiennych. Proces ten umożliwia funkcja file_exists.
W przypadku gdy strona nie zawiera wielu plików powyższy proces można uprościć korzystając z tablicy zawierającej dostępne nazwy plików.
$files = array( 'about', 'contact', 'sth' ); if( !in_array( $_GET['file'], $files ) ) die( 'Podany plik nie istnieje' );
Jeśli ilość plików jest większa niż w powyższym przykładzie można go zoptymalizować korzystając z tablicy wielowymiarowej zamiast funkcji in_array.
$files = array( 'about' => 1, 'contact' => 1, 'sth' => 1 ); if( $files[ $_GET['file'] ] !== 1 ) die( 'Podany plik nie istnieje' );
Zadanie: bezpieczne przechowywanie plików
Opis: W wielu aplikacjach występuje konieczność przechowywania plików, do których nie powinno być bezpośredniego dostępu. Mogą to być logi, statystyki czy chociażby pliki downloadu, do których dostęp powinien być tylko przez odpowiednią stronę.
Niebezpieczeństwo: Bezpośredni dostęp do plików.
Rozwiązania:
1. Umieszczenie plików ponad katalogiem public_html.
Katalog public_html jest domyślnym katalogiem, który przechowuje pliki serwera Apache. Umieszczając pliki ponad tym katalogiem mamy pewność, że żaden użytkownik nie uzyska do nich dostępu za pomocą przeglądarki internetowej.
2. Ograniczenie dostępu do katalogu.
W przypadku niektórych serwerów (np. home.pl) powyższa metoda nie zadziała. W takim przypadku najlepiej skorzystać z pliku .htaccess i całkowicie zablokować dostęp do katalogu. Można tego dokonać korzystając z poniższego wpisu.
deny from all
Tagi: bezpieczeństwo plików, directory traversal
Kategoria: Bezpieczeństwo, Bezpieczeństwo aplikacji internetowych
Nie html_public, a public_html zwykle ;-)
Warto zauważyć, że nie każdy hosting – choć raczej powinienem powiedzieć, że bardzo rzadko to się w ogóle zdarza – umożliwia trzymanie danych (i dostęp do nich) poza public_html.
he he tak to jest jak się pisze notke po nieprzespanej nocy ;p
a jeśli chodzi o hostingi to jak na razie na kożdym mogłem, z wyjątkiem home
Zamiast 1b proponowałbym basename() i później doklejenie rozszerzenia, ale rzeczywiście metoda z tablicą asocjacyjną bardziej mi się podoba :)
dodane :-)
Michał, widać zależy to od klasy hostingów z jakimi masz do czynienia – ja mam z tymi tanimi, a home to widziałem dwa razy w życiu i zawsze od strony FTP ;-)
Oj tam :-) Powiem tak na Netlooku, Home i za przeproszeniem i365 taki myk działa :-) a na te co nie działa to jest wersja z .htaccess :-)
Na home.pl wystarczy przekierować swoją domenę na utworzony wcześniej katalog i tym samym mamy identyczne rozwiązanie jak z "public_html".
Nie do końca, jeśli domena.pl podepniesz pod katalog domena to skrypty w nim umieszczone nie będą miały dostępu do katalogu nadrzędnego. Sprawdzałem to do tego jakiś pan na chacie potwierdził to :-) Po prosu ich serwer HTTP jest tak zaprojektowany.