Filtrowanie danych w PHP
Udostępniając formularz na stronie internetowej, bądź przykładowo API dajemy użytkownikom możliwość wysłania do nas dowolnych danych. Zresztą samo posiadanie strony jest już czynnikiem ryzyka związanym z atakami, bo różne skrypty na stronie wysyłają i przyjmują różne zapytania z zewnątrz.
Jedną z ważnych elementów tworzenia jakichkolwiek aplikacji jest walidacja, bądź sanityzacja danych. W skrócie polega ona na tym, że zanim zapiszemy cokolwiek w bazie, bądź wykonamy operacje na jakichkolwiek danych przekazanych pośrednio lub bezpośrednio przez użytkownika, powinniśmy sprawdzić czy aby na pewno ten wysłał to co założyliśmy.
Przykładowo – jeżeli udostępniamy na stronie formularz w którym użytkownik wpisuje adres e-mail to istotna jest weryfikacja czy aby na pewno użytkownik podał adres e-mail., a nie kod w SQL lub cokolwiek innego. Wyobraźmy sobie taki scenariusz.
- Udostępniamy użytkownikowi możliwość wpisania adresu e-mail w formularzu.
- Następnie zapisujemy adres email w taki sposób (w zmiennej
$emailmamy to co wpisał użytkownik)INSERT INTO users (email) VALUES ('$email') - Dowcipny użytkownik jednak zamiast prawidłowego adresu e-mail wpisuje
'); DELETE FROM users; -- - To powoduje, że wykonujemy taką komendę:
INSERT INTO users (email) VALUES (''); DELETE FROM users; --') --to komentarz w SQL, więc to co jest po nim nie będzie brane pod uwagę, a cała operacje może skutkować usunięciem wszystkich użytkowników w bazie.
Łatwo sobie wyobrazić, że w ten sposób można bardzo dużo – dodać się do bazy, pobierać dane itd.
Filtry
PHP udostępnia zestaw dedykowanych funkcji do filtrowania danych. Dzielą się na dwa typy:
- walidujące, czyli sprawdzanie czy dane są takie jak być powinny
- sanityzujące, czyli sprawiające by dany były takie jakich oczekujemy (np. usunięcie niedozwolonych znaków itp.)
Do obsługi obu typów służy funkcja filter_var.
filter_var(mixed $value, int $filter = FILTER_DEFAULT, array|int $options = 0): mixed$value – wartość, którą sprawdzamy / poprawiamy
$filter – jeden z kilkudziesięciu dostępnych filtrów np. e-mail, adres strony internetowej itd.
$options – dodatkowe opcje lub flagi np. zezwól na polskie znaki w adresie e-mail. Przekazujemy je w tablicy z kluczem options i flags. Dodatkowo dostępna jest opcja default, która pozwala na zwrócenie dowolnej wartości zamiast false.
Przykład użycia
$params = [
'options' => [
'default' => 0,
'min_range' => 1,
'max_range' => 1000
],
'flags' => FILTER_FLAG_ALLOW_HEX | FILTER_FLAG_ALLOW_OCTAL
];
filter_var ('555.44', FILTER_VALIDATE_INT, array|int $options = 0): mixedIstnieje jeszcze tablicowa odmiana funkcji – filter_var_array, która przyjmuje tablicę wartości, a nie pojedynczą wartość.
Filtry walidujące
Filtry walidujące sprawdzają wartość i zwracają true (prawidłowa), false (nieprawidłowa) lub null (jeżeli jest ustawiona flaga w opcjach FILTER_NULL_ON_FAILURE. Idea z tą flagą jest taka, że sprawdzany jest format zwracanych danych np. jak oczekujemy wartości „true”, „1”, „on”, „false”, „0”, „off” to otrzymamy null, jeżeli żadna z tych wartości nie zostanie zwrócona. Bez flagi otrzymalibyśmy zawsze false. Poza flagą niektóre filtry mają również dodatkowe opcje (np. możliwość ustawienia minimalnej i maksymalnej wartości) oraz opcję default w której przekazujemy co ma zwrócić funkcja zamiast false.
FILTER_VALIDATE_EMAIL– sprawdza czy adres e-mail ma prawidłowy format. Dostępna jest flagaFILTER_FLAG_EMAIL_UNICODE, która zezwala na używanie np. polskich znaków w adresie.FILTER_VALIDATE_URL– sprawdza czy adres URL ma prawidłowy format. Dodatkowo możemy ustawić flagiFILTER_FLAG_PATH_REQUIREDorazFILTER_FLAG_QUERY_REQUIRED, które sprawdzają odpowiednio czy adres zwiera ścieżkę (znak / np.example.com/index.php) oraz zapytanie (? np.index.php?query=test).FILTER_VALIDATE_DOMAIN– sprawdza czy adres domeny ma prawidłowy format. Włączenie flagiFILTER_FLAG_HOSTNAMEdodatkowo sprawdza czy zaczyna się od litery alfabetu lub cyfry oraz zawiera tylko takie znaki lub łączniki (np.-)FILTER_VALIDATE_IP– sprawdza czy wartość jest adresem IP. Przy użyciu flag możemy ograniczyć zakres sprawdzania adresu:FILTER_FLAG_IPV4,FILTER_FLAG_IPV6,FILTER_FLAG_NO_PRIV_RANGE,FILTER_FLAG_NO_RES_RANGE,FILTER_FLAG_GLOBAL_RANGE,FILTER_VALIDATE_MAC– sprawdza czy wartość jest adresem MAC.FILTER_VALIDATE_BOOL– zwracatruedla wartości1,"true"lub"on"– przydatne jak korzystamy np. z JavaScript do przesyłania danych typubooleanFILTER_VALIDATE_INT– sprawdza czy wartość jest typu int. W opcjach możemy ustawić minimalną (min_range) i maksymalną (max_range) wartość. Dodatkowe flagi pozwalają też na przekazanie wartości w systemie szesnastkowym (FILTER_FLAG_ALLOW_HEX)lub ósemkowym (FILTER_FLAG_ALLOW_OCTAL)FILTER_VALIDATE_FLOAT– sprawdza czy wartość jest typu float. W opcjach możemy dodatkowo ustawić minimalną (min_range) i maksymalną (max_range) wartość oraz znak rozdzielający część ułamkową np. przecinek zamiast kropki (opcjadecimal). Ponadto dostępna jest flagaFILTER_FLAG_ALLOW_THOUSAND, która pozwala na rozdzielanie części tysięcznych np. możliwy wtedy jest taki zapis13,111,753.479.FILTER_VALIDATE_REGEXP– sprawdza czy adres pasuje do wyrażenia regularnego. Wyrażenie podajemy w opcjiregexp.
Filtry sanityzujące
Filtry sanityzujące służą do poprawienia danych (np. usuwają zbędne znaki) i zwracają zmienione dane, które potem możemy np. poddać pod filtr walidujący. Korzysta się z nich tak samo jak z filtrów walidujących z tą różnicą, że nie ma tutaj dostępnych opcji – są tylko dodatkowe flagi. Na końcu wyjaśnimy niektóre flagi, które pojawiają się w wielu funkcjach.
FILTER_DEFAULT,FILTER_UNSAFE_RAW– domyślny filtr, który nie robi nic, ale umożliwia ustawienie dodatkowych flag. które omówimy na końcuFILTER_FLAG_STRIP_LOW,FILTER_FLAG_STRIP_HIGH,FILTER_FLAG_STRIP_BACKTICK,FILTER_FLAG_ENCODE_LOW,FILTER_FLAG_ENCODE_HIGH,FILTER_FLAG_ENCODE_AMPFILTER_SANITIZE_EMAIL– usuwa wszystkie znaki specjalne oprócz dozwolonych w adresie e-mailFILTER_SANITIZE_ENCODED– usuwa lub zamienia znaki w adresie url, działa podobnie dourlencode(). Dostępne flagi toFILTER_FLAG_STRIP_LOW,FILTER_FLAG_STRIP_HIGH,FILTER_FLAG_STRIP_BACKTICK,FILTER_FLAG_ENCODE_LOW,FILTER_FLAG_ENCODE_HIGHFILTER_SANITIZE_ADD_SLASHES– dodaje ukośniki przed znakami',"",\,null. Działa tak jakaddslashes.FILTER_SANITIZE_NUMBER_FLOAT– usuwa wszystkie znaki za wyjątkiem cyfr i znaków+-. Dostępne flagi pozwalają na zwiększenie zakresu:FILTER_FLAG_ALLOW_FRACTION– kropka jako znak rozdzielający część ułamkową,FILTER_FLAG_ALLOW_THOUSAND– rozdzielenie części tysięcznych przecinkiem,FILTER_FLAG_ALLOW_SCIENTIFIC– używanieelubEjak naukowa notacjaFILTER_SANITIZE_NUMBER_INT– usuwa wszystkie znaki za wyjątkiem cyfr i znaków plus i minus.FILTER_SANITIZE_SPECIAL_CHARS– zamienia znaki'"<>&i znaki sterujące na format HTML-a. Dostępne filtry:FILTER_FLAG_STRIP_LOW,FILTER_FLAG_STRIP_HIGH,FILTER_FLAG_STRIP_BACKTICK,FILTER_FLAG_ENCODE_HIGHFILTER_SANITIZE_FULL_SPECIAL_CHARS– zamienia znaki'"<>&i znaki sterujące na format HTML-a. Działa tak jak funkcjahtmlspecialcharsz włączoną flagąENT_QUOTES. W przypadku wykrycia znaku spoza domyślnie ustawionego kodowania zwrócony zostanie pusty łańcuch. FiltrFILTER_FLAG_NO_ENCODE_QUOTESpowoduje, że znaki'i""nie będą kodowane. Na serwerach często ta flaga jest włączona domyślnie.FILTER_SANITIZE_URL– usuwa wszystkie znaki oprócz liter, cyfr i używanych w adresie url.
Niektóre flagi są używane przez wiele funkcji. Flagi ze STRIP w nazwie – usuwają, a te z ENCODE w nazwie – zamieniają.
FILTER_FLAG_STRIP_LOW,FILTER_FLAG_ENCODE_LOW– usuwa / koduje znaki sterujące (ASCII < 32)FILTER_FLAG_STRIP_HIGH,FILTER_FLAG_ENCODE_HIGH– usuwa / koduje znaki z górnej połowy tabeli ASCI (ASCII > 127)FILTER_FLAG_STRIP_BACKTICK,FILTER_FLAG_ENCODE_BACKTICK– usuwa / koduje znak cofania.
Filtrowanie odpowiedzi z serwera
Do filtrowania danych przychodzących może posłużyć funkcja filter_input, która w pierwszym parametrze przyjmuje typ danych, a w drugim nazwę pola. W kolejnych parametrach podajemy filtr i opcje (jak w poprzednich funkcjach).
INPUT_GET– sprawdzana jest zmienna globalna http$_GETINPUT_POST– sprawdzana jest zmienna globalna http$_POSTINPUT_COOKIE– sprawdzana jest zmienna globalna ciasteczek$_COOKIEINPUT_SERVER– sprawdzana jest zmienna globalna serwera$_SERVERINPUT_ENV– sprawdzana jest środowiskowa zmienna globalna$_ENV
Przykładowo dla adresu http://example.com?test=1 , filter_input(INPUT_GET, 'test') zwróci domyślnie wartość true. Oczywiście możemy stosować filtry i opcje tak jak w poprzedniej funkcji i uzyskać poprawiony łańcuch. Jeżeli zmienna nie istnieje zostanie zwrócona wartość null.
Podobnie jak poprzednio, dostępny jest tablicowy odpowiednik funkcji – filter_input_array, który przyjmuje tablicę w pierwszym argumencie.
Walidacja z użyciem tablicowego odpowiednika
Funkcja filter_var_array pozwala na filtrowanie wielu danych jednocześnie.
Możemy ustawić jedną flagę dla wszystkich danych przekazanych w tablicy, albo ustawić tablicę z filtrami, która będzie odpowiadała konkretnym danym.
filter_var_array(
[ 'nick' => 'Eskim',
'age' => 199,
'email' => 'email@example.com',
],
[ 'nick' => [
'filter' => FILTER_SANITIZE_SPECIAL_CHARS,
'flags' => FILTER_FLAG_STRIP_BACKTICK | FILTER_FLAG_ENCODE_HIGH,
],
'age' => [
'filter' => FILTER_VALIDATE_INT,
'flags' => FILTER_FLAG_STRIP_BACKTICK | FILTER_FLAG_ENCODE_HIGH,
'options' => ['min_range' => 3, 'max_range' => 150]
],
'email' => [
'filter' => FILTER_VALIDATE_EMAIL
],
]
]
);Zapytania SQL
Przed wysłaniem zapytania do bazy powinniśmy również podać je sanityzacji. Do tego służy specjalna funkcja prepare, która występuje chyba we wszystkich bibliotekach do obsługi zapytań w PHP (choć oczywiście może się nieco różnić składnią). Najpierw używamy prepare, a potem dopiero wykonujemy zapytanie. Implementacja tej funkcjonalności różni się w zależności od biblioteki, ale cel pozostaje ten sam.
PDO
PDO jest najpopularniejszą biblioteką PHP używaną do operowania na bazie danych. Zanim wyślemy zapytanie zamieniamy je na obiekt i przed wysłaniem (lub w trakcie) wpisujemy parametry do obiektu.
$prep = $db->prepare (' SELECT id FROM users WHERE name = :username');
$prep->bindParam('username', $username, PDO::PARAM_STR);
$prep->execute();W powyższym przykładzie tworzy zapytanie z parametrem username pod który później podpinamy zmienną typu string $username. Robimy to za pomocą bindParam. Oczywiście zmiennych może być więcej. Parametry można także przekazać bezpośrednio przy wywoływaniu metody execute.
HTML Purifier
HTML Purifier to biblioteka filtrująca napisana w PHP, która służy do oczyszczania kodu HTML usuwając potencjalnie niebezpieczne treści i upewniając się, że wynikowy kod HTML jest poprawny z punktu widzenia norm i standardów. Biblioteka jest zaprojektowana w taki sposób, aby chronić przed różnego rodzaju atakami, takimi jak ataki XSS (Cross-site Scripting):
- oczyszcza kod, usuwając niebezpieczne tagi, atrybuty i skrypty,
- upewnia się, że wynikowy kod HTML jest zgodny ze standardami określonymi przez W3C
Możesz dostosować działanie biblioteki do swoich potrzeb wybierając, które tagi czy atrybuty chcesz zachować, a które usunąć. Potrafi również przekształcać niektóre tagi lub atrybuty w inne na przykład przekształcając wszystkie odnośniki target="_blank" w rel="noopener", aby były bardziej bezpieczne. Biblioteka obsługuje nie tylko fragmenty kodu HTML, ale również całe dokumenty, w tym DOCTYPE, HTML i BODY.
Jeśli planujesz osadzać treści użytkowników na swojej stronie internetowej lub aplikacji, HTML Purifier to jedno z narzędzi, które warto rozważyć w celu zapewnienia bezpieczeństwa i zgodności wynikowego kodu HTML.
Co dalej?
Powyższe nie wyczerpuje tematu, który jest znacznie szerszy i obejmuje każdy rodzaj danych, który pobieramy z dowolnego zewnętrznego źródła – pliku, przez internet, itd. Najważniejsze, by zawsze o tym pamiętać i zakładać najgorszy scenariusz.
Podsumowanie
- Dane uzyskane od użytkownika powinny być filtrowane.
- Sanityzację i walidację danych można przeprowadzić wykorzystując funkcję
filter_varlub jej tablicowego odpowiednikafilter_var_array. - Filtry walidujące sprawdzają poprawność podanych danych np. adres e-mail.
- Filtry sanityzujące poprawiają dane na format, który nas interesuje np. usuwają znaki specjalne.
- Do filtrowania danych przychodzących możemy skorzystać z funkcji
filter_input. - Biblioteki do obsługi baz danych oferują zwykle dedykowane funkcje do sanityzacji danych.






