Wielokrotnie początkujący programiści PHP zastanawiają się, która z tych funkcji jest lepsza. Mają one dużo wspólnych elementów, jednakże w różnych środowiskach sprawdzają się one raz lepiej, raz gorzej.
Zacznijmy od tego, co właściwie te funkcje nam dają. Otóż obie pozwalają nam na odkażanie i sprawdzanie poprawności zmiennych, w tym danych przychodzących od użytkowników. I tu kłania się podstawowa zasada: „Nigdy nie ufaj użytkownikom”, a właściwie danym, które Ci użytkownicy przesyłają do naszych aplikacji PHP.
Ale czy nie można tych czynności, jak na przykład sprawdzanie poprawności, robić z poziomu HTLM5 i JavaScript? Można, a nawet trzeba, jednakże na tym poziomie ma to na celu wspomóc użytkownika przy wprowadzaniu danych, a nie zabezpieczyć serwer.
No dobrze, ale jak użyć tych funkcji? Wyobraźmy sobie taki kod:
<?php
/*
fragment tworzący połączenie do bazy danych
*/
$sql = 'SELECT author, content FROM posts WHERE id = ' . $_GET['id'] . ';';
/*
fragment wykonujący zapytanie do bazy danych
*/
Nie będzie żadnego problemu, jeżeli użytkownik odwiedzi stronę:
https://nasz_serwer/?id=44
Ponieważ zapytanie, które się wykona, będzie wyglądać tak:
SELECT author, content FROM posts WHERE id = 44;
Jednakże, gdyby ktoś wysłał zapytanie takiej treści:
https://nasz_serwer/?id=44%3B+DROP+TABLE+posts
Wtedy do bazy danych poleci zapytanie:
SELECT author, content FROM posts WHERE id = 44; DROP TABLE posts;
I tak oto staliśmy się ofiarą ataku SQL Injection, co może przysporzyć nam niemałego problemu. I tu uwaga, specjalnie pomijam tutaj możliwość weryfikacji danych i powiązywania wartości z poziomu pdo czy mysqli. Na potrzeby tego wpisu, zapytania SQL będziemy wykonywać bezpośrednio, czego absolutnie nie wolno robić w aplikacjach „produkcyjnych”.
Aby ustrzec się przed atakiem w tej formie, użyjemy funkcji filter_input, zatem nasz kod będzie wyglądał tak:
<?php
/*
fragment tworzący połączenie do bazy danych
*/
$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
if ($id != NULL) {
$sql = 'SELECT author, content FROM posts WHERE id = ' . $id . ';';
/*
fragment wykonujący zapytanie do bazy danych
*/
} else {
/*
fragment z obsługą błędu (wyświetlający błąd lub podejmujący inne działania)
*/
}
Oczywiście poza „INPUT_GET, 'id'”, odpowiadającemu $_GET[’id’], mamy również opcje INPUT_POST, INPUT_COOKIE, INPUT_SERVER oraz INPUT_ENV. Jako trzeci parametr zaś został podany filtr. Dzielą się one na trzy grupy: sprawdzające poprawność, odkażające oraz inne. Pełny ich spis dostępny jest w dokumentacji. W zaprezentowanym przykładzie został użyty filtr, którego zadaniem jest usunięcie wszystkich znaków, które nie są cyfrą.
No dobrze, a czy nie możemy do zabezpieczenia tego zapytania użyć filter_var? Oczywiście, że możemy. Przyjmuje on te same filtry, które przyjmuje filter_input, zatem przykładowy kod wyglądałby tak:
<?php
/*
fragment tworzący połączenie do bazy danych
*/
$id = filter_var($_GET['id'], FILTER_SANITIZE_NUMBER_INT);
if ($id != "") {
$sql = 'SELECT author, content FROM posts WHERE id = ' . $id . ';';
/*
fragment wykonujący zapytanie do bazy danych
*/
} else {
/*
fragment z obsługą błędu (wyświetlający błąd lub podejmujący inne działania)
*/
}
W momencie, gdy parametr „id” w zapytaniu występuje i ma on przynajmniej jedną cyfrę, to wynik działania obu tych funkcji jest ten sam. Różnice zaczynają się pojawiać, gdy w zapytaniu tego parametru nie ma.
Gdy parametru id w zapytaniu nie ma, funkcja filter_input zwróci NULL, zaś funkcja filter_var zwróci pusty string, dodatkowo wywołując ostrzeżenie, gdyż próbujemy dostać się w tablicy $_GET do elementu z nieistniejącym indeksem. Możemy się oczywiście przed tym ostatnim zabezpieczyć używając funkcji isset lub filter_has_var, jednakże jedynie dostosowujemy swój kod do funkcji, która nie jest stworzona do tego, do czego chcemy jej użyć.
I w tym momencie dochodzimy do odpowiedzi na pytanie z tytułu. Do obsługi zapytań HTTP powinno się używać filter_input, bo do tego ta funkcja została stworzona. A filter_var? Tę funkcję powinniśmy stosować do sprawdzania zmiennych istniejących w naszym kodzie, lub otrzymywanych w wyniku wcześniejszego przetwarzania danych wejściowych.
Bibliografia: