Jak sprawdzić w PHP czy załadowany plik to na pewno obraz ?

Jak wiadomo wszelkiego rodzaju operacje pozwalające użytkownikom naszej strony/systemu webowego na interakcję w postaci wprowadzania własnych treści/multimediów są poza zaletą również zagrożeniem dla naszej aplikacji. W tym wpisie zajmiemy się sytuacją kiedy przykładowo pozwalamy użytkownikowi wgrać własne logo/avatar w ramach naszej nazwijmy to ogólnie e-usługi. Oczywiste jest, że podczas uploadu trzeba skorzystać z funkcji is_uploaded_file(), aby uniknąć „file upload attacka”, ale co dalej ? Jak sprawdzić czy przesyłany plik to faktycznie poprawny obraz ?

A właściwie możesz sobie zadać pytanie po co w ogóle to sprawdzać ? Załóżmy więc, że użytkownik tworzy plik PHP ze złośliwym kodem, który może np. kasować pliki z serwera, wysyłać jakieś pliczki do nas (np. z konfiguracją bazy danych), uruchamiać nieskończone pętle, wysyłać spam czy tworzyć w nieskończoność pliki na serwerze – wiadomo możliwości jest bardzo dużo.

Żeby taki plik wgrać na nasz serwer przez formularz uploadu obrazu nasz złośliwiec nazywa sobie plik jakasNazwa.php#.jpg.

Podstawowe pytanie jak nasz system zapisuje i udostępniamy takie pliki, przykładowo :
1). nie zwracamy uwagi na nazwę pliku wgrywanego tylko sami ją generujemy – zakładamy na podstawie sprawdzenia rozszerzenia, że plik jest poprawny i zapisujemy go w postaci losowaNazwa.jpg. Teoretycznie więc uruchamiając z poziomu przeglądarki owy plik przez adres typu http://naszsystem.pl/upload/losowaNazwa.jpg serwer nie powinien w żaden sposób takiego pliku wykonać, więc złośliwy kod w nim zawarty jest nieszkodliwy. Jednak tak jak napisałem – teoretycznie, bo w praktyce bywa to różnie
2). zapisujemy plik nieistotne pod jaką nazwą w katalogu, który z pomocą pliku .htaccess nie pozwala na bezpośredni dostęp do jego zawartości. W ten sposób znając ścieżkę do złośliwego pliku nie ma opcji aby taki plik uruchomić, a w momencie próby jego pobrania przez sam system – robimy to przez odpowiedni skrypt – opcja na pewno bardzo przyzwoita
3). ufamy użytkownikowi bezgranicznie i zapisujemy plik w takiej samej postaci czyli de fakto użytkownik będzie miał możliwość uruchomienia pliku http://naszsystem.pl/upload/jakasNazwa.php#.jpg. I tutaj może (ale nie musi) skończyć się tragicznie, bo przez zwykłe wpisanie hasha w nazwie pliku powodujemy, że plik zostanie potraktowany jako PHP, a dalej dla przeglądarki to po prostu kotwica. Tutaj też jednak kwestia leży po stronie ustawień na serwerze.

Nie wnikając już jak dokładnie zapisujemy i udostępniamy pliki z uploadu skupmy się na tym jak faktycznie sprawdzić czy przesłany obraz to plik JPG. Na myśl przychodzą takie sposoby :

// sposób 1
if(strtolower(end(explode('.',$_FILES[upload][name])))!='jpg')
{
  echo 'Przesyłany plik nie jest obrazem JPG !'; exit();
}
// sposób 2
$pathinfo = pathinfo($_FILES[upload][name]);
if(strtolower($pathinfo['extension'])!='jpg')
{
  echo 'Przesyłany plik nie jest obrazem JPG !'; exit();
}
// sposób 3
if(!in_array($_FILES[upload][type],array('image/jpeg','image/jpg','image/pjpeg')))
{
  echo 'Przesyłany plik nie jest obrazem JPG !'; exit();
}

Niestety dla naszego przykładowego spreparowanego pliku jakasNazwa.php#.jpg każde z powyższych rozwiązań „da się oszukać” i nawet jeżeli z racji specyfiku serwera/aplikacji tak wgrany plik w żaden sposób nie jest w stanie nam zaszkodzić to warto po prostu nie chcieć żeby ktoś nam wgrywał takie śmieci.

Chcemy więc najnormalniej sprawdzić czy przesłany plik to faktycznie poprawny obraz. Możliwości jest wiele, w sieci również znajdziecie niejedno rozwiązanie, a tymczasem z mojej strony dwa przykładowe :

// sposób 1
if(exif_imagetype($plik)!=IMAGETYPE_JPEG)
{
  echo 'Przesyłany plik nie jest obrazem JPG !'; exit();
}
// sposób 2
if(getimagesize($plik)==false)
{
  echo 'Przesyłany plik nie jest obrazem JPG !'; exit();
}

O ile z pierwszym rozwiązaniem możecie mieć problem z racji nie włączonego rozszerzenia php_exif na Waszym serwerze to drugie z wykorzystaniem getimagesize powinno zadziałać „wszędzie”.

W ten sposób sprawdzimy czy faktycznie użytkownik przesyła odpowiedni plik i nie damy się „zwieść” takim rozwiązaniom jak pierwsze 3 przedstawione. Warto również pamiętać o odpowiednim nazewnictwie pliku w momencie wykonywania move_uploaded_file – nazwę możemy np. losować/generować lub tworzyć na podstawie oryginalnej, którą wcześniej np. filtrujemy wyrażeniami regularnymi aby wyrzuć różnego rodzaju „dziwne znaczki”.

VN:F [1.9.22_1171]
Rating: 4.8/5 (5 votes cast)
VN:F [1.9.22_1171]
Rating: +2 (from 2 votes)
Jak sprawdzić w PHP czy załadowany plik to na pewno obraz ?, 4.8 out of 5 based on 5 ratings

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *


dziewięć − 6 =

Możesz użyć następujących tagów oraz atrybutów HTML-a: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>