Zagrożenia bezpieczeństwa
Bazy danych często zawierają wrażliwe dane i umożliwiają wykonywanie niebezpiecznych operacji. Dla bezpiecznej pracy z Nette Database kluczowe są następujące aspekty:
- Zrozumienie różnicy między bezpiecznym i niezabezpieczonym API
- Używanie sparametryzowanych zapytań
- Właściwa walidacja danych wejściowych
Czym jest SQL Injection?
Wstrzyknięcie kodu SQL jest najpoważniejszym zagrożeniem bezpieczeństwa podczas pracy z bazami danych. Występuje, gdy niefiltrowane dane wejściowe użytkownika stają się częścią zapytania SQL. Atakujący może wstawić własne polecenia SQL i w ten sposób
- wyodrębnić nieautoryzowane dane
- zmodyfikować lub usunąć dane w bazie danych
- Ominąć uwierzytelnianie
To samo dotyczy eksploratora baz danych:
Bezpieczne zapytania parametryzowane
Podstawową obroną przed SQL injection są sparametryzowane zapytania. Nette Database zapewnia kilka sposobów ich wykorzystania.
Najprostszym sposobem jest użycie znaków zapytania:
Dotyczy to wszystkich innych metod w Database Explorer, które umożliwiają wstawianie wyrażeń z symbolami zastępczymi znaków zapytania i parametrami.
W przypadku klauzul INSERT
, UPDATE
lub WHERE
można przekazać wartości w tablicy:
Walidacja wartości parametrów
Zapytania parametryzowane są podstawą bezpiecznej pracy z bazami danych. Jednak wartości przekazywane do nich muszą przejść kilka poziomów walidacji:
Sprawdzanie typu
Zapewnienie prawidłowego typu danych parametrów jest krytyczne – jest to warunek konieczny do bezpiecznego korzystania z Nette Database. Baza danych zakłada, że wszystkie dane wejściowe mają prawidłowy typ danych odpowiadający kolumnie.
Na przykład, jeśli $name
w poprzednich przykładach nieoczekiwanie stał się tablicą zamiast łańcuchem,
Nette Database spróbuje wstawić wszystkie jego elementy do zapytania SQL, co spowoduje błąd. Dlatego nigdy nie używaj
niezwalidowanych danych z $_GET
, $_POST
lub $_COOKIE
bezpośrednio w zapytaniach do bazy
danych.
Walidacja formatu
Drugi poziom sprawdza format danych – na przykład upewniając się, że ciągi są zakodowane w UTF-8, a ich długość jest zgodna z definicją kolumny lub sprawdzając, czy wartości liczbowe mieszczą się w dopuszczalnym zakresie dla typu danych kolumny.
Na tym poziomie można częściowo polegać na samej bazie danych – wiele baz danych odrzuca nieprawidłowe dane. Jednak zachowanie może się różnić: niektóre mogą cicho obcinać długie ciągi lub przycinać liczby, które są poza zakresem.
Walidacja specyficzna dla domeny
Trzeci poziom obejmuje kontrole logiczne specyficzne dla danej aplikacji. Na przykład sprawdzenie, czy wartości z pól wyboru pasują do dostępnych opcji, czy liczby mieszczą się w oczekiwanym zakresie (np. wiek 0–150 lat) lub czy relacje między wartościami mają sens.
Zalecane metody walidacji
- Użyj Nette Forms, które automatycznie obsługują prawidłową walidację wszystkich danych wejściowych.
- Użyj Presenters i zadeklaruj typy danych parametrów w metodach
action*()
irender*()
. - Lub zaimplementować niestandardową warstwę walidacji przy użyciu standardowych narzędzi PHP, takich jak
filter_var()
.
Bezpieczna praca z kolumnami
W poprzedniej sekcji omówiliśmy, jak prawidłowo walidować wartości parametrów. Jednak w przypadku korzystania z tablic w zapytaniach SQL, taką samą uwagę należy zwrócić na ich klucze.
W przypadku poleceń INSERT i UPDATE jest to poważna luka w zabezpieczeniach – atakujący może wstawić lub zmodyfikować
dowolną kolumnę w bazie danych. Może na przykład ustawić is_admin = 1
lub wstawić dowolne dane do wrażliwych
kolumn (znane jako Mass Assignment Vulnerability).
W warunkach WHERE jest to jeszcze bardziej niebezpieczne, ponieważ mogą one zawierać operatory:
Atakujący może wykorzystać to podejście do systematycznego odkrywania wynagrodzeń pracowników. Mogą zacząć od zapytania o pensje powyżej 100 000, następnie poniżej 50 000, a stopniowo zawężając zakres, mogą ujawnić przybliżone pensje wszystkich pracowników. Ten rodzaj ataku nazywany jest wyliczaniem SQL.
Metody where()
i whereOr()
są jeszcze bardziej elastyczne i obsługują wyrażenia SQL, w tym operatory
i funkcje, zarówno w kluczach, jak i wartościach. Daje to atakującemu możliwość wykonania złożonego
wstrzyknięcia SQL:
Atak ten kończy oryginalny warunek za pomocą 0)
, dołącza własny SELECT
za pomocą
UNION
w celu uzyskania wrażliwych danych z tabeli users
i zamyka poprawnym składniowo zapytaniem za
pomocą WHERE (1)
.
Biała lista kolumn
Do bezpiecznej pracy z nazwami kolumn potrzebny jest mechanizm, który zapewnia, że użytkownicy mogą wchodzić w interakcje tylko z dozwolonymi kolumnami i nie mogą dodawać własnych. Próba wykrycia i zablokowania niebezpiecznych nazw kolumn (czarna lista) jest zawodna – atakujący zawsze może wymyślić nowy sposób na napisanie niebezpiecznej nazwy kolumny, której nie przewidziałeś.
Dlatego znacznie bezpieczniej jest odwrócić logikę i zdefiniować jawną listę dozwolonych kolumn (whitelisting):
Dynamiczne identyfikatory
W przypadku dynamicznych nazw tabel i kolumn należy użyć symbolu zastępczego ?name
. Zapewnia to prawidłową
ucieczkę identyfikatorów zgodnie z podaną składnią bazy danych (np. przy użyciu backticks w MySQL):
Ważne: Symbolu ?name
należy używać tylko w przypadku zaufanych wartości zdefiniowanych w kodzie aplikacji. W
przypadku wartości dostarczonych przez użytkownika należy ponownie użyć białej listy. W
przeciwnym razie istnieje ryzyko wystąpienia luk w zabezpieczeniach: