Autor wpisu: Athlan, dodany: 28.05.2011 14:40, tagi: sql, php, internet
Dziś bardzo krótko, bez zbędnych dywagacji, czyli tylko i wyłącznie o tablicy $_SERVER
. Dbając o bezpieczeństwo aplikacji webowych zwraca się uwagę na wiele czynników, jakimi są SQL injections, przechwytywanie nieprawidłowych parametrów, uogólniające zapytania przepuszczające maskę % w LIKE zapytaniu do baz, XSS‘y w $_POST
, $_GET
.
I finalnie… wiele osób zapomina (a jeszcze więcej nie jest tego świadom) o możliwości wstrzyknięcia szkodliwych danych w $_SERVER['HTTP_X_FORWARDED_FOR']
;. Konsekwencje są oczywiście katastrofalne.
O ile sama walidacja jest rzeczą wtórną, diabeł tkwi w trzech szczegółach:
- Rzecz trywialna, ale pamiętajmy, że w naturalnym procesie użytkowania przeglądarki, w nagłówku może zostać zwrócony nie tylko jeden adres IP, a kilka oddzielonych przecinkiem, w tym localhost‘y (standard nagłówka
X-Forwarded-For
). - Wstrzyknięcie Javascriptów jest możliwe, ale notabene najmniej szkodliwe, bo do spreparowania nagłówka potrzebny jest bardziej zaawansowany proces (dajmy na to Data Tamping, który przedstawię poniżej), np. niż wklejenie syfu w linku/obrazku i przesłanie go komuś przez komunikator, żeby wykraść jego ciasteczka sesyjne
document.cookie
i przesłać je sobie na serwer w dowolny sposób, zatem atakowi nie ulegną osoby trzecie. - Niepoprawność danych, które można zmanipulować, jest chyba rzeczą oczywistą: nieprzepuszczenie takich danych przez filtry może skutkować złymi wartościami zwracanymi np. przez
ip2long()
i zapis w zupełności nieprzydatnych nam później danych do bazy. - … ale największe nieprzyjemności możemy mieć przez spreparowanie lewych zapytań do baz danych, o ile nie używamy sprawdzonych ORM lub czegokolwiek, co pomaga nam filtrować wartości do niej przekazywane i używane w warunkach zapytań (data binding).
Przykład tampingu danych, żeby spreparować niepożądane efekty.
Mamy bardzo prosty, niebezpieczny kod funkcji, która pobiera pierwszy adres na liście adresów oddzielonych przecinkami z $_SERVER['HTTP_X_FORWARDED_FOR']
o ile istnieje, natomiast w przeciwnym wypadku $_SERVER['REMOTE_ADDR']
:
< ?php function getUserIp() { if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) return trim(current(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']))); return $_SERVER['REMOTE_ADDR']; } $sUserIP = getUserIp(); echo 'Hi "' . $sUserIP . '"!'; // first bug while display. var_dump(ip2long($sUserIP)); // second bug while transforming data. ?>
Pora na przykład manipulacji takich danych.
- Będziemy używać Tamper Data dla Firefox’a.Dość popularny wśród developerów addon do Firefox’a, pozwala zmodyfikować dane
$_POST
,$_GET
,$_COOKIE
, nagłówki, “w locie żądania” etc. - Po instalacji w menu Narzędzia pojawi się pozycja Dane Tamper, która uruchamia okienko do podsłuchiwania żądań. Po kliknięciu Rozpocznij podsłuchujemy wszystkie wychodzące żądania z naszej przeglądarki. Każde żądanie nie zostanie przepuszczone, dopóki go nie zmanipulujemy klikając Tamper, lub przepuścimy dalej klikając Wyślij.
- Jeżeli zdecydujemy się Tamper’ować żądanie, naszym oczom ukaże się okno z parametrami. Klikamy prawym przyciskiem myszy na listę parametrów, wybieramy Dodaj i wpisujemy nasz przykładowy, brzydki dla aplikacji nagłówek:
X_FORWARDED_FOR=<script>alert('Test.')</script>
Naszym oczom ukazują się co najmniej dwa błędy. Pierwszy to błąd prezentacji danych, który wykorzystuje <script>
. O ile nie musimy się tym przejmować, bo naturalnie takie żądania nie są tak łatwo wysyłane, użytkownik nie może paść ofiarą ataku przez kliknięcie w link, który np. ukradnie mu ciasteczka. Dane nagłówkowe nie są w stanie być zmodyfikowane poprzez kliknięcie w link, podobnie jak z danymi $_POST
(oczywiście mówimy o przypadkach trywialnych, bez javascript’owych wymuszanych submitów targetowanych do np. ramek).
Znacznie poważniejszym błędem jest konsekwencja wadliwego formatu danych, które nasza funkcja bagatelizuje. Po pierwsze mamy fałszywe dane zwracane przez ip2long()
, po drugie kto powiedział, że właśnie z tej funkcji korzystamy, a nie zapisujemy danych plain’em i nie bindujemy pofiltrowanych danych lub instrukcji warunkowych zapytania przez np. sprawdzony ORM.
Rozwiązanie problemu.
Edit: Jak słusznie zauważył Zyx, zapomniałem o tym wspomnieć, że skoro mogą znaleźć się tam dowolne dane przesłane od użytkownika, nie należy tego pola traktować jako wyznacznik, że jest to numer jego IP, jest ono bezużyteczne i powoduje potencjalną lukę. Poza zabezpieczeniami to podstawowy argument, żeby o polu zapomnieć i używać $_SERVER['REMOTE_ADDR']
.