PHP – poznał go prawie każdy programista, który kiedykolwiek próbował napisać stronę internetową, trochę bardziej skomplikowaną niż statyczny HTML. Jest prosty, łatwy w nauce i wybaczający wiele błędów. Niestety wraz z prostotą idą błędy, które dość mocno przysłużyły się opinii, że jest to wymarzony cel ataków hakerskich.
Większość początkujących, ale i także część zaawansowanych programistów nie zdaje sobie sprawy jak poprawnie używać języka PHP, aby uniknąć podstawowych podatności na ataki, które mogą wpłynąć na bezpieczeństwo wystawionej w Internecie aplikacji. Częściowo też sam język nie pomaga nam w rozwiązaniu tych problemów, a tylko powoduje w nas fałszywe poczucie bezpieczeństwa. Przykładem tego może być to w jaki sposób PHP wspomaga programistów w tak podstawowej funkcjonalności jak walidacja danych wejściowych (ang. input validation – IV) i enkodowanie wyjściowe (ang. output encoding), które sa podstawową techniką chroniącą przed podatnościami takimi jak XSS – Cross Site Scripting i wstrzykiwanie kodu (Injection).
Tak dla przypomnienia XSS to podatność umożliwiająca wywołanie kodu JavaScript na przeglarce użytkownika, co może służyć np. do przejęcia sesji użytkownika (poprzez zdalne odczytywanie cistek z sesjami), bądź nawet całego komputera (patrz atak z wykorzystaniem narzędzia BeEF). Wstrzykiwanie umożliwia wywoływanie nieporządanych poleceń po stronie serwera, np. w warstwie powłoki (bash) lub na bazie danych (SQLInjection).
Jak napisałem wyżej podstawą techniką ochrony przed tego typu atakami jest sprawdzanie tego, co nam użytkownik wysyła do aplikacji. Zasada jest prosta:
„Każde dane wejściowe, które mogą być zmodyfikowane na zewnątrz aplikacji, muszą by traktowane jako potencjalnie niebezpieczne”
Tak więc, za dane wejściowe traktujemy nie tylko dane wprowadzane w polach formularzy, ale również wartości nagłówków HTTP (zwłaszcza niestandardowych) czy wartości w ciasteczkach (ang. cookies). Chronić powinniśmy się przede wszystkim przed danymi, które w swojej treści zawierają fragmenty kodu HTML i JS, skryptów bash czy zapytań SQL.
Jak PHP pomaga nam chronić się przed nieporządanymi wartościami? Można by powiedzieć, że nie daje nam wiele. Przede wszystkim należy zwrócić uwagę na to, że PHP jest językiem dynamicznie typowanym. Co to znaczy? Język ten nie porównuje typu wartości i typu zmiennej do jakiej te wartości są przypisywane. Weryfikacja następuje dopiero wtedy gdy chcemy na tych danych wykonać jakąś operację. Zwykle silne typowanie jest pierwszą linią obrony podczas weryfikacji wprowadzanych danych, a jego brak powoduje problem, gdzie nie możemy jednoznacznie zaznaczyć, że wskazana zmienna ma być na przykład tylko wartością całkowitą, lub np. typem binarnym.
Innym sposobem walidacji danych wejściowych jest poddawanie ich niestandardowym testom. Programista jest zmuszony zbudować własne weryfikatory wprowadzanych danych, np. opartych na whitelistach lub wyrażeniach regularnych (choć tutaj istnieją również techniki omijania takich testów), dzięki czemu „wpuszcza” do swojej aplikacji tylko wartości takie jakich się może spodziewać.
Warta uwagi, choć rozwiązująca tylko jeden z szeregu problemów, jest funkcja strip_tags()
. Funkcja ta pozwala na usunięcie z przekazanej wartości wszelkie tagi HTML. Niestety zastosowanie jej nie chroni przed atakami XSS, gdzie wstrzykiwany niebezpieczny kod jest w pola atrybutów tagów HTML (np. w formularzach). Wtedy poradzić możemy sobie tylko whitelistą dozwolonych znaków.
Co zrobić jeśli jednak dane wejściowe obejdą filtry? Wtedy pomóc nam może tzw. encodowanie. Encodowanie zamienia nam znaki danych wejścowych w taki sposób, aby były poprawnie wyświetlane w docelowym środowisku (np. na stronie HTML, na wyświetlaczu elektronicznym) oraz, aby nie wpływały na sposób renderowania (np. nie były interpretowane jako kod HTML, JS, czy jako operacje sterujące).
Co w kwestii encodowania daje nam PHP? Pierwsza strona w wynikach google podaje nam głównie użycie funkcji htmlspecialchars()
. Funkcja zamienia znaki specjalne na tzw. encje HTML (np. „<" na "<
„, „>” na „>
„, patrz: htmlspecialchars). Tę samą czynność wykonuje inna funkcja PHP – htmlentities()
(patrz: htmlentities(), z tym, że enkoduje ona wszystkie znaki, które mają swoje odpowiedniki w encjach HTML, a nie tylko znaki specjalne. Niestety funkcje te nie przydadzą się gdy w naszych danych musimy dopuścić znaki specjalne, aby umożliwić np. wproqwadzane bardziej skomplikowanych treści (np. nazwiska z apostrofami, czy prosty kod HTML). Tutaj pomogą nam tylko whitelisty.
Ogólnie PHP jest ubogi jeśli chodzi o ochronę aplikacji przed wprowadzanymi danymi i wymaga od programisty dość dużej uwagi a także powtarzalnej pracy do wykonania, aby zaimplementować odpowiednie techniki ochrony (mechanizm template’ów). Powoduje to, że proste aplikacje, często nie są chronione w odpowiedni sposób i podatne na szereg, nawet dość prostych ataków. Mam nadzieję, że wkrótce będe mógł opisać jeden przykład z życia na jaki miałem okazję się natknąć.