Co to jest SQL Injection?
Autor: Redakcja Domeny.ForSale Opublikowano
W skrócie: SQL Injection to podatność, która pozwala zmienić działanie zapytania do bazy danych przez odpowiednio przygotowane dane wejściowe. Powstaje przede wszystkim wtedy, gdy aplikacja skleja polecenia SQL z tekstem podanym przez użytkownika. Podstawową ochroną są zapytania parametryzowane, które oddzielają treść polecenia od jego danych.
Jak dane użytkownika stają się poleceniem
SQL to język używany do odczytywania i zmieniania danych w relacyjnych bazach danych. Aplikacja może za jego pomocą wyszukać konto, pobrać zamówienie albo zapisać nowy adres klienta. SQL Injection, czyli wstrzyknięcie SQL, występuje wtedy, gdy dane wejściowe zostają potraktowane jako część składni takiego polecenia.
Załóżmy, że aplikacja wyszukuje konto po loginie. Programista wpisuje początek zapytania, dokleja wartość z formularza i zamyka ją apostrofem. Zwykły login daje oczekiwany wynik, ale tekst zawierający apostrof i dodatkowy warunek może zmienić sens całego wyszukiwania.
Źródłem niebezpiecznej wartości nie musi być formularz logowania. Mogą nim być również parametr adresu URL, filtr produktów, nagłówek żądania lub dane wcześniej zapisane w bazie. Istotne jest to, jak aplikacja wykorzystuje tę wartość, a nie skąd ją otrzymała.
Prosty eksperyment na Linuxie
Poniższy przykład działa wyłącznie na tymczasowej bazie w pamięci procesu. Nie łączy się z żadną stroną ani zewnętrznym serwerem. Pokazuje odczyt nadmiarowych rekordów, a nie kompletny mechanizm logowania.
Potrzebujesz Python 3 z dostępnym modułem sqlite3. Sprawdzisz go w terminalu poleceniem:
python3 -c "import sqlite3; print(sqlite3.sqlite_version)"
Jeśli polecenie się nie powiedzie, sposób instalacji Python 3 lub brakującego modułu sprawdź w dokumentacji swojej dystrybucji. Nie trzeba uruchamiać osobnego serwera bazy danych.
Uruchom w terminalu cały blok:
python3 - <<'PY'
import sqlite3
polaczenie = sqlite3.connect(":memory:")
polaczenie.execute(
"CREATE TABLE konta (id INTEGER PRIMARY KEY, login TEXT)"
)
polaczenie.executemany(
"INSERT INTO konta (login) VALUES (?)",
[("anna",), ("piotr",)]
)
wartosc = "' OR 1=1 -- "
zapytanie = "SELECT id, login FROM konta WHERE login = '" + wartosc + "'"
print("Zapytanie:", zapytanie)
print("Wynik:", polaczenie.execute(zapytanie).fetchall())
polaczenie.close()
PY
Wynik zawiera oba konta, mimo że żadne nie ma loginu odpowiadającego podanej wartości. Apostrof zamyka tekst loginu, OR 1=1 dodaje warunek zawsze prawdziwy, a -- rozpoczyna komentarz, przez co końcowy apostrof nie wpływa już na zapytanie.
To celowo podatny kod do lokalnej demonstracji. Nie sprawdzaj podobnych wejść w cudzych serwisach bez wyraźnej zgody obejmującej zakres testów.
Jak naprawić ten sam kod
Zapytanie parametryzowane zawiera znaczniki miejsc na dane, a same wartości przekazuje się oddzielnie do biblioteki obsługującej bazę. W module sqlite3 takim znacznikiem może być ?. Biblioteka traktuje przekazaną wartość jako dane, nie jako dodatkową składnię SQL.
W poprzednim przykładzie zastąp budowanie i wykonanie zapytania tym fragmentem, pozostawiając go przed polaczenie.close():
zapytanie = "SELECT id, login FROM konta WHERE login = ?"
wynik = polaczenie.execute(zapytanie, (wartosc,)).fetchall()
print("Wynik:", wynik)
Teraz otrzymasz pustą listę: baza szuka dosłownego loginu zawierającego cały przekazany tekst. Gdy zmienisz wartosc na "anna", zapytanie zwróci tylko konto Anny. Przecinek w (wartosc,) tworzy jednoelementową krotkę, której oczekuje ten sposób przekazania parametrów.
Nie zastępuj parametrów ręcznym usuwaniem apostrofów ani formatowaniem tekstu, na przykład przez f-string. Login z apostrofem może być poprawną daną; to aplikacja ma bezpiecznie go obsłużyć. Składnia parametrów zależy od biblioteki, więc wzorca ? nie należy bez sprawdzenia przenosić do każdego sterownika.
Jakie mogą być skutki podatności
SQL Injection może umożliwić odczyt cudzych danych, obejście części kontroli dostępu, zmianę rekordów lub ich usunięcie. Faktyczny skutek zależy od zapytania, silnika bazy i uprawnień konta aplikacji. Samo wykrycie podatności nie oznacza automatycznie przejęcia całego serwera.
Brak widocznego błędu także nie dowodzi bezpieczeństwa. Aplikacja może ukrywać komunikaty bazy, a zmienione działanie ujawniać jedynie różnicą odpowiedzi. Z kolei pojedynczy błąd po wpisaniu apostrofu jest sygnałem do analizy, a nie samodzielnym dowodem SQL Injection.
Co sprawdzić we własnej aplikacji
Przegląd zacznij od miejsc, w których powstają zapytania SQL:
- Zastąp sklejanie tekstu parametrami dla wszystkich wartości pochodzących spoza zapytania.
- Sprawdź surowe zapytania także wtedy, gdy używasz ORM, czyli warstwy mapującej obiekty aplikacji na dane w bazie.
- Nazwy kolumn i kierunek sortowania wybieraj z zamkniętej listy dozwolonych opcji. Parametry wartości zwykle nie zastępują takich elementów składni.
- Ogranicz uprawnienia konta bazy do operacji potrzebnych aplikacji.
- Nie pokazuj użytkownikom szczegółowych błędów bazy ani pełnych zapytań zawierających poufne dane.
Na początek wybierz jedno wyszukiwanie korzystające z danych użytkownika. Dodaj test ze zwykłą wartością oraz tekstem zawierającym apostrof i sprawdź, czy oba wejścia pozostają danymi, zamiast zmieniać strukturę zapytania.
Zobacz też: Co to jest ETF?
