SQL Injection w tworzeniu aplikacji mobilnych: co to jest, metody ataków i ochrona

Autor: IT Sectr Opublikowano: 2026-04-06 Czas czytania: 9 min

SQL Injection — typ ataku na bazę danych aplikacji, w którym osoba atakująca wstrzykuje złośliwy kod SQL do parametrów zapytania, uzyskując nieautoryzowany dostęp do danych lub możliwość ich modyfikacji. Według danych OWASP (2025), SQL Injection pozostaje jedną z krytycznych podatności, mogącą doprowadzić do całkowitej kompromitacji bazy danych. Wstrzyknięcie kodu SQL pozwala osobie atakującej odczytywać, modyfikować i usuwać rekordy, a w niektórych przypadkach — uzyskać dostęp do systemu operacyjnego serwera.

Najważniejsze

  • SQL Injection — wstrzyknięcie kodu SQL przez parametry użytkownika, zmieniające logikę zapytania do bazy danych
  • Parametryzacja zapytań — główna metoda ochrony: oddzielenie kodu SQL od danych za pomocą prepared statements
  • Typy ataków — klasyczne wstrzyknięcie (przez WHERE), Blind SQLi (wnioskowanie logiczne), UNION-based (odczyt obcych tabel)
  • Error-based SQLi — wydobycie danych przez komunikaty błędów bazy danych
  • ORM-frameworki — zmniejszają ryzyko SQLi przy prawidłowym użyciu, ale nie eliminują go całkowicie

Co to jest SQL Injection?

SQL Injection to podatność powstająca, gdy aplikacja tworzy zapytanie SQL poprzez konkatenację ciągów z danymi użytkownika. Osoba atakująca przekazuje do parametru zapytania specjalnie skonstruowany ciąg znaków, który zmienia strukturę polecenia SQL. Zamiast być danymi, wprowadzony ciąg staje się częścią kodu SQL, co pozwala atakującemu wykonywać dowolne zapytania do bazy danych. Według raportu Verizon Data Breach Report (2025), SQL Injection występuje w 8% wszystkich zbadanych wycieków danych.

Dlaczego SQL Injection jest nadal aktualny?

Pomimo znajomości podatności (pierwsze wzmianki — koniec lat 90.), SQL Injection występuje w nowoczesnych aplikacjach. Przyczyną jest czynnik ludzki: programiści dopuszczają konkatenację ciągów w kodzie, legacy-kod nie jest refaktoryzowany, a ORM-frameworki są używane nieprawidłowo (np. surowe zapytania z podstawianiem wartości przez ciągi). Według danych Veracode (2026), około 14% wszystkich przeskanowanych aplikacji zawiera co najmniej jedną podatność SQLi.

Co może zrobić osoba atakująca?

Udane wstrzyknięcie SQL daje atakującemu szeroki zakres możliwości: odczyt dowolnych tabel bazy danych, w tym skrótów haseł i danych osobowych użytkowników; modyfikacja i usuwanie rekordów; wykonywanie operacji administracyjnych (DROP TABLE, TRUNCATE); w niektórych konfiguracjach — zdalne wykonywanie poleceń przez xp_cmdshell (MSSQL) lub INTO OUTFILE (MySQL). Konsekwencje — od wycieku danych użytkowników do całkowitej utraty kontroli nad systemem.

Rodzaje wstrzyknięć SQL

Wstrzyknięcia SQL klasyfikuje się według sposobu wydobywania danych z bazy. Wybór metody zależy od tego, jak aplikacja przetwarza wyniki zapytania i komunikaty błędów. Klasyfikacja OWASP wyróżnia trzy główne typy: In-band (dane wydobywane tym samym kanałem), Inferential/Blind (wnioskowanie logiczne) i Out-of-band (dane przesyłane innym kanałem).

TypSposób wydobycia danychZłożonośćCzęstotliwość
In-band (klasyczny)Bezpośrednio przez wynik zapytaniaNiskaWysoka
Blind SQLiWnioskowanie logiczne na podstawie odpowiedzi serweraWysokaŚrednia
Out-of-bandPrzez zewnętrzny kanał (DNS, HTTP)ŚredniaNiska

In-band SQL Injection (klasyczne)

Najczęstszy typ. Osoba atakująca wstrzykuje kod SQL do parametru zapytania, a wynik wstrzyknięcia jest widoczny bezpośrednio w odpowiedzi serwera. Dwa podtypy: Error-based (przez komunikaty błędów BD) i UNION-based (przez operator UNION SELECT). Error-based wykorzystuje informacje z komunikatów błędów, na przykład błąd składni MySQL, który może ujawnić nazwę tabeli lub strukturę zapytania. UNION-based pozwala połączyć wyniki legalnego zapytania z danymi z innych tabel bazy danych.

Blind SQL Injection (ślepe)

Stosowane, gdy aplikacja nie wyświetla wyników zapytania ani komunikatów błędów. Osoba atakująca zadaje pytania typu „tak/nie”, wysyłając zapytania z warunkami logicznymi i analizując różnicę w odpowiedzi serwera (np. czas odpowiedzi lub zawartość strony). Time-based Blind SQLi wykorzystuje funkcję opóźnienia (SLEEP, WAITFOR DELAY) do potwierdzenia warunków — jeśli strona ładuje się dłużej, warunek jest prawdziwy. Ta metoda jest bardzo wolna — wydobycie jednego rekordu może zająć godziny.

python
# Przykład Blind SQL Injection (time-based)
# Jeśli SQLi jest podatny, SLEEP(2) wykonuje się pod warunkiem
import requests
import time

payload = "' OR IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a',SLEEP(2),0) -- "
url = "http://example.com/user?id=1" + payload

start = time.time()
response = requests.get(url)
elapsed = time.time() - start

# Jeśli odpowiedź przyszła po >2 sekundach — pierwsza litera hasła to 'a'
print(f"Time: {elapsed:.2f}s - First letter is 'a'" if elapsed > 2 else "First letter is not 'a'")

Out-of-band SQL Injection

Dane są przesyłane nie przez odpowiedź HTTP, ale przez alternatywne kanały: zapytania DNS, żądania HTTP do zewnętrznego serwera, SMTP. Stosowane, gdy aplikacja nie zwraca wyników zapytania i nie pokazuje błędów. MySQL obsługuje funkcję LOAD_FILE(), która może zainicjować zapytanie DNS, a MSSQL — xp_dirtree do wysyłania danych na zdalny serwer SMB. Out-of-band SQL Injection jest skuteczny, ale wymaga dodatkowych ustawień na serwerze atakującego i obecności określonych funkcji BD.

Jak działa wstrzyknięcie SQL?

Mechanizm SQL Injection opiera się na tym, że SQL używa cudzysłowów dla literałów ciągów. Jeśli aplikacja podstawia dane wejściowe użytkownika bezpośrednio do zapytania SQL bez escapowania, osoba atakująca może „zamknąć” ciąg i dodać dowolny kod SQL. Na przykład w zapytaniu SELECT * FROM users WHERE name = '$input' podstawienie ' OR '1'='1 zamienia zapytanie na SELECT * FROM users WHERE name = '' OR '1'='1', co zwraca wszystkich użytkowników.

Klasyczny przykład: ominięcie uwierzytelniania

Rozważmy formularz logowania z zapytaniem SELECT * FROM users WHERE username = '$user' AND password = '$pass'. Jeśli osoba atakująca wprowadzi w polu username admin' -- , a hasło pozostawi puste, wynikowe zapytanie staje się SELECT * FROM users WHERE username = 'admin' -- ' AND password = ''. Znaki -- komentują resztę zapytania, a warunek sprawdzania hasła jest wyłączony. Serwer zwraca rekord użytkownika admin, a atakujący loguje się do systemu bez znajomości hasła.

python
# Przykład SQL Injection — ominięcie uwierzytelniania
# PODATNY KOD: bezpośrednia konkatenacja ciągów
def login_vulnerable(username, password):
    query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
    cursor.execute(query)
    return cursor.fetchone() is not None

# username = "admin' --" zeruje sprawdzanie hasła
# BEZPIECZNA WERSJA: zapytanie sparametryzowane
def login_secure(username, password):
    query = "SELECT * FROM users WHERE username = %s AND password = %s"
    cursor.execute(query, (username, password))
    return cursor.fetchone() is not None

UNION-based: odczyt obcych tabel

Operator UNION SELECT pozwala połączyć wyniki dwóch zapytań SELECT. Jeśli osoba atakująca znajdzie podatny parametr, dodaje UNION SELECT z zapytaniem z innej tabeli. Na przykład: ' UNION SELECT username, password FROM admins --. Warunkiem powodzenia jest zgodność liczby kolumn w obu zapytaniach. Liczbę kolumn określa się przez ORDER BY (podstawienie ' ORDER BY 1--, następnie 2, 3... aż do błędu). Znając liczbę kolumn, atakujący podstawia UNION SELECT z taką samą liczbą pól.

SQL Injection w aplikacjach mobilnych

Aplikacje mobilne spotykają się z SQL Injection zarówno po stronie serwerowej (API), jak i po stronie klienta — w lokalnych bazach danych (SQLite, Realm). Chociaż ryzyko SQLi po stronie serwera w mobilnym API jest podobne do aplikacji webowych, lokalna BD tworzy dodatkowy wektor. Jeśli aplikacja przechowuje dane w SQLite i wykonuje zapytania z konkatenacją ciągów, złośliwe dane, które trafiły do lokalnej BD przez API, mogą spowodować SQLi przy późniejszym przetwarzaniu.

SQL Injection w SQLite na Android/iOS

Lokalna baza SQLite na urządzeniu jest również podatna na SQL Injection, jeśli aplikacja tworzy zapytania przez konkatenację ciągów. Content Providers na Androidzie i Core Data na iOS domyślnie używają parametryzacji, ale surowe zapytania wymagają uwagi programisty. SQLite nie obsługuje wielu zapytań przez średnik, co ogranicza możliwości atakującego, ale nie chroni przed odczytem danych przez warunki WHERE. Zawsze używaj selectionArgs w Androidzie i NSPredicate z parametrami w iOS.

SQL Injection przez API aplikacji mobilnej

API, do którego odwołuje się aplikacja mobilna, jest podatne tak samo jak każdy serwer webowy. Deweloperzy mobilni często uważają, że SQLi to problem tylko backendu, ale podatność powstaje na poziomie endpointu API, który przyjmuje parametry od klienta. Podział odpowiedzialności nie chroni: jeśli programista backendu zapomniał sparametryzować zapytanie, aplikacja mobilna użytkownika staje się wektorem ataku. Wymagaj od backendu używania ORM lub prepared statements.

kotlin
// Przykład SQL Injection w lokalnym SQLite na Androidzie
// PODATNY KOD: bezpośrednia konkatenacja
fun getUserVulnerable(db: SQLiteDatabase, userId: String): Cursor {
    return db.rawQuery(
        "SELECT * FROM users WHERE id = $userId", null
    )
}

// BEZPIECZNA WERSJA: parametryzacja przez selectionArgs
fun getUserSecure(db: SQLiteDatabase, userId: String): Cursor {
    return db.rawQuery(
        "SELECT * FROM users WHERE id = ?",
        arrayOf(userId)
    )
}

Metody zapobiegania wstrzyknięciom SQL

Ochrona przed SQL Injection opiera się na prostej zasadzie: nigdy nie ufaj danym wejściowym użytkownika w zapytaniach SQL. Jedyną niezawodną metodą jest parametryzacja zapytań (prepared statements), w której kod SQL i dane są przesyłane oddzielnie. Wszystkie inne metody — escapowanie, walidacja, WAF — są dodatkowymi warstwami ochrony, ale nie zastępują parametryzacji. Według danych OWASP (2025), parametryzacja zapobiega 100% ataków SQL Injection.

Prepared Statements (zapytania sparametryzowane)

Podczas używania prepared statements zapytanie SQL jest najpierw kompilowane przez serwer BD bez danych, a następnie wartości parametrów są przesyłane osobno. Baza danych traktuje parametry jako dane, a nie jako wykonywalny kod. Nawet jeśli osoba atakująca przekaże ' OR '1'='1, BD interpretuje to jako wartość ciągu, a nie jako kod SQL. Prepared statements są obsługiwane przez wszystkie nowoczesne języki i frameworki: PDO w PHP, PreparedStatement w Javie, cursor.execute w Pythonie.

ORM-frameworki i Query Buildery

Nowoczesne ORM (Hibernate, Entity Framework, SQLAlchemy, Room) automatycznie używają parametryzacji podczas wykonywania zapytań, jeśli programista nie przechodzi na surowe zapytania. Jednak ORM nie chronią w pełni: konstrukcje takie jak @Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true) w JPA wymagają przekazywania parametrów przez nazwane parametry, a nie konkatenację. Query Buildery (Knex, jOOQ) również parametryzują zapytania domyślnie, jeśli nie używa się surowych metod.

Escapowanie ciągów (niezalecane jako główna metoda)

Escapowanie znaków specjalnych (mysql_real_escape_string) to przestarzała metoda, która nie chroni przed wszystkimi typami SQL Injection. Problem: escapowanie zależy od kodowania i może być ominięte przy użyciu wielobajtowych kodowań (np. GBK w systemach azjatyckich). Używaj escapowania tylko w legacy-kodzie, gdzie parametryzacja jest niemożliwa, i zawsze w połączeniu ze ścisłą walidacją typu danych wejściowych.

MetodaEfektywnośćZalecenie
Prepared Statements100%Obowiązkowe dla wszystkich zapytań
ORM (prawidłowe użycie)99%Zalecane
Escapowanie ciągów70% (zależy od kodowania)Tylko legacy
Walidacja wejścia (white-list)50% (tylko dla liczb)Dodatkowo
WAF (Web Application Firewall)60%Dodatkowo

Zasada najmniejszych uprawnień dla BD

Konto aplikacji powinno mieć minimalnie niezbędne uprawnienia: SELECT, INSERT, UPDATE, DELETE — tylko do tych tabel, które są rzeczywiście potrzebne aplikacji. Zabroń używania DROP, TRUNCATE, CREATE dla konta aplikacyjnego. Ograniczy to szkody nawet w przypadku udanego SQL Injection: atakujący nie będzie mógł usunąć tabel ani wykonać operacji administracyjnych.

Narzędzia do wykrywania wstrzyknięć SQL

Regularne testowanie na SQL Injection powinno być częścią pipeline bezpiecznego rozwoju. Połączenie analizy statycznej, skanowania dynamicznego i ręcznego pentestu daje najlepsze wyniki. Według raportu Synopsys Cybersecurity Report (2025), automatyczne skanery znajdują do 70% podatności SQLi, jednak złożone ataki Blind wymagają ręcznego testowania.

  • sqlmap — najpopularniejsze narzędzie do automatycznego wykrywania i wykorzystywania SQL Injection
  • OWASP ZAP — darmowy skaner DAST z modułami aktywnego skanowania SQLi
  • Burp Suite Scanner — profesjonalne narzędzie z automatycznym wykrywaniem SQL Injection
  • SonarQube — statyczna analiza kodu pod kątem podatności, w tym wzorców SQLi
  • CodeQL — semantyczna analiza kodu do wyszukiwania wstrzyknięć SQL w źródłach

Dla aplikacji mobilnych ważna jest również analiza lokalnego SQLite: sprawdź wszystkie wywołania rawQuery, zapytania ContentProvider i zapytania Room z rawQuery. Narzędzia: Android Studio Lint (wykrywa SQLi w SQLite), MobSF (Mobile Security Framework) do automatycznej statycznej i dynamicznej analizy APK/IPA. Zaleca się również testowanie endpointów API przez sqlmap z proxy przechwytującym ruch aplikacji mobilnej.

Często zadawane pytania

Jaka jest różnica między SQL Injection a NoSQL Injection?

SQL Injection atakuje relacyjne bazy danych przez zapytania SQL. NoSQL Injection oddziałuje na nierelacyjne BD (MongoDB, Couchbase) przez ich operatory zapytań ($gte, $ne, $where). W MongoDB wstrzyknięcie jest możliwe, jeśli aplikacja tworzy dokument BSON z ciągu JSON. Mechanizmy ochrony są podobne: parametryzacja i walidacja typów.

Jak samodzielnie wykryć SQL Injection w kodzie?

Znajdź wszystkie miejsca, w których zapytania SQL są tworzone przez konkatenację ciągów z danymi użytkownika. Szukaj wzorców takich jak "SELECT ... WHERE id = " + userId lub f"UPDATE ... SET name = '{name}'". Każdy taki ciąg to potencjalne wstrzyknięcie SQL. Zastąp wszystkie sparametryzowanymi zapytaniami lub prepared statements.

Czy ORM automatycznie chroni przed SQL Injection?

ORM-frameworki chronią automatycznie tylko wtedy, gdy używasz ich metod Query Builder i nazwanych parametrów. Jeśli używasz surowych zapytań (nativeQuery w JPA, rawQuery w Room), ochrona nie działa — należy przekazywać parametry przez przygotowane wyrażenia, a nie przez konkatenację ciągów.

Co to jest Second-Order SQL Injection?

Second-Order SQL Injection to atak, w którym złośliwe dane są zapisywane w BD jako bezpieczne, ale następnie używane w innym zapytaniu bez escapowania. Na przykład atakujący rejestruje się z username ' OR '1'='1. Dane są zapisywane jako ciąg — przy rejestracji nie ma ataku. Ale jeśli inne zapytanie podstawia username do SQL bez parametryzacji, wstrzyknięcie działa.

Czy NoSQL Injection może być groźniejsze niż SQL Injection?

NoSQL Injection jest potencjalnie groźniejsze ze względu na mniejszą świadomość programistów. Programiści wiedzą o SQL Injection i większość używa ORM, ale o NoSQL Injection wie niewielu. W MongoDB nieprawidłowo sformułowane zapytanie może zwrócić wszystkie dokumenty kolekcji. Ochrona — te same prepared statements (parametryzacja BSON) i ścisła walidacja danych wejściowych.

Podsumowanie

  • SQL Injection — atak polegający na wstrzyknięciu kodu SQL przez nieescapowane parametry użytkownika w zapytaniach do BD
  • Główne typy — In-band (klasyczne), Blind (ślepe, czasowe), Out-of-band (przez zewnętrzny kanał)
  • Parametryzacja zapytań — jedyna 100% niezawodna metoda ochrony przed SQL Injection
  • Mit o ORM — ORM chroni tylko przy użyciu wbudowanych metod, surowe zapytania z konkatenacją są nadal podatne
  • Zasada najmniejszych uprawnień — ograniczenie uprawnień konta BD minimalizuje szkody przy udanym ataku
  • Regularne testowanie — sqlmap, OWASP ZAP, SonarQube powinny być częścią pipeline CI/CD
  • Lokalny SQLite — aplikacje mobilne również powinny parametryzować zapytania do lokalnej BD

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również