SQL Injection — typ útoku na databázi aplikace, při kterém útočník vkládá škodlivý SQL kód do parametrů dotazu, čímž získává neoprávněný přístup k datům nebo možnost jejich modifikace. Podle údajů OWASP (2025) zůstává SQL Injection jedním z kritických zranitelností, které mohou vést k úplnému kompromitování databáze. Injekce SQL kódu umožňuje útočníkovi číst, měnit a mazat záznamy a v některých případech — získat přístup k operačnímu systému serveru.
Hlavní body
SQL Injection je zranitelnost, která vzniká, když aplikace vytváří SQL dotaz pomocí zřetězení řetězců s uživatelskými daty. Útočník odesílá do parametru dotazu speciálně vytvořený řetězec, který mění strukturu SQL příkazu. Místo dat se vložený řetězec stává součástí SQL kódu, což umožňuje útočníkovi provádět libovolné dotazy do databáze. Podle Verizon Data Breach Report (2025) je SQL Injection přítomen v 8 % všech zkoumaných úniků dat.
Navzdory známosti zranitelnosti (první zmínky — konec 90. let) se SQL Injection vyskytuje v moderních aplikacích. Důvod — lidský faktor: vývojáři připouštějí zřetězení řetězců v kódu, legacy kód není refaktorován a ORM frameworky jsou používány nesprávně (např. raw dotazy s dosazováním hodnot pomocí řetězců). Podle údajů Veracode (2026) obsahuje přibližně 14 % všech skenovaných aplikací alespoň jednu zranitelnost SQLi.
Úspěšný SQL Injection poskytuje útočníkovi širokou škálu možností: čtení libovolných tabulek databáze, včetně hashů hesel a osobních údajů uživatelů; modifikace a mazání záznamů; provádění administrativních operací (DROP TABLE, TRUNCATE); v některých konfiguracích — vzdálené provádění příkazů přes xp_cmdshell (MSSQL) nebo INTO OUTFILE (MySQL). Následky — od úniku uživatelských dat až po úplnou ztrátu kontroly nad systémem.
SQL injekce se klasifikují podle způsobu získávání dat z databáze. Volba metody závisí na tom, jak aplikace zpracovává výsledky dotazu a chybová hlášení. Klasifikace OWASP rozlišuje tři hlavní typy: In-band (data jsou získávána stejným kanálem), Inferential/Blind (logické závěry) a Out-of-band (data jsou přenášena jiným kanálem).
| Typ | Způsob získávání dat | Složitost | Četnost |
|---|---|---|---|
| In-band (klasický) | Přímo přes výsledek dotazu | Nízká | Vysoká |
| Blind SQLi | Logické závěry podle odpovědí serveru | Vysoká | Střední |
| Out-of-band | Přes externí kanál (DNS, HTTP) | Střední | Nízká |
Nejrozšířenější typ. Útočník vkládá SQL kód do parametru dotazu a výsledek injekce je přímo viditelný v odpovědi serveru. Dva podtypy: Error-based (přes chybová hlášení databáze) a UNION-based (přes operátor UNION SELECT). Error-based využívá informace z chybových hlášení, například syntaktickou chybu MySQL, která může odhalit název tabulky nebo strukturu dotazu. UNION-based umožňuje spojit výsledky legitimního dotazu s daty z jiných tabulek databáze.
Používá se, když aplikace nezobrazuje výsledky dotazu nebo chybová hlášení. Útočník klade otázky typu „ano/ne”, odesílá dotazy s logickými podmínkami a analyzuje rozdíl v odpovědi serveru (např. dobu odezvy nebo obsah stránky). Time-based Blind SQLi používá funkci zpoždění (SLEEP, WAITFOR DELAY) k potvrzení podmínek — pokud se stránka načítá déle, podmínka je pravdivá. Tato metoda je velmi pomalá — získání jednoho záznamu může trvat hodiny.
# Příklad Blind SQL Injection (time-based)
# Pokud je SQLi zranitelný, SLEEP(2) se provede za podmínky
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
# Pokud odpověď přišla po >2 sekundách — první písmeno hesla je 'a'
print(f"Time: {elapsed:.2f}s - First letter is 'a'" if elapsed > 2 else "First letter is not 'a'")
Data jsou přenášena ne pomocí HTTP odpovědi, ale prostřednictvím alternativních kanálů: DNS dotazů, HTTP požadavků na externí server, SMTP. Používá se, když aplikace nevrací výsledky dotazu a nezobrazuje chyby. MySQL podporuje funkci LOAD_FILE(), která může iniciovat DNS dotaz, a MSSQL — xp_dirtree pro odesílání dat na vzdálený SMB server. Out-of-band SQL Injection je účinný, ale vyžaduje další nastavení na serveru útočníka a přítomnost určitých funkcí databáze.
Mechanismus SQL Injection je založen na tom, že SQL používá uvozovky pro řetězcové literály. Pokud aplikace dosazuje uživatelský vstup přímo do SQL dotazu bez escapování, útočník může „uzavřít” řetězec a přidat libovolný SQL kód. Například v dotazu SELECT * FROM users WHERE name = '$input' dosazení ' OR '1'='1 mění dotaz na SELECT * FROM users WHERE name = '' OR '1'='1', což vrací všechny uživatele.
Uvažujme přihlašovací formulář s dotazem SELECT * FROM users WHERE username = '$user' AND password = '$pass'. Pokud útočník zadá do pole username admin' -- a heslo nechá prázdné, výsledný dotaz se stane SELECT * FROM users WHERE username = 'admin' -- ' AND password = ''. Znaky -- komentují zbytek dotazu a podmínka kontroly hesla je deaktivována. Server vrátí záznam uživatele admin a útočník vstoupí do systému bez znalosti hesla.
# Příklad SQL Injection — obejití autentizace
# ZRANITELNÝ KÓD: přímé zřetězení řetězců
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' --" ruší kontrolu hesla
# BEZPEČNÁ VERZE: parametrizovaný dotaz
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
Operátor UNION SELECT umožňuje spojit výsledky dvou SELECT dotazů. Pokud útočník najde zranitelný parametr, přidá UNION SELECT s dotazem z jiné tabulky. Příklad: ' UNION SELECT username, password FROM admins --. Podmínkou úspěchu je shodný počet sloupců v obou dotazech. Počet sloupců se určuje pomocí ORDER BY (dosazení ' ORDER BY 1--, poté 2, 3... až do chyby). Útočník, který zná počet sloupců, dosadí UNION SELECT se stejným počtem polí.
Mobilní aplikace se setkávají s SQL Injection jak na straně serveru (API), tak na straně klienta — v lokálních databázích (SQLite, Realm). Ačkoli riziko serverového SQLi v mobilním API je podobné webovým aplikacím, lokální databáze vytváří dodatečný vektor. Pokud aplikace ukládá data v SQLite a provádí dotazy se zřetězením řetězců, škodlivá data, která se dostala do lokální databáze přes API, mohou při následném zpracování způsobit SQLi.
Lokální databáze SQLite na zařízení je také zranitelná vůči SQL Injection, pokud aplikace vytváří dotazy zřetězením řetězců. Content Providers na Androidu a Core Data na iOS používají parametrizaci ve výchozím nastavení, ale raw dotazy vyžadují pozornost vývojáře. SQLite nepodporuje více dotazů přes středník, což omezuje možnosti útočníka, ale nechrání před čtením dat pomocí podmínek WHERE. Vždy používejte selectionArgs v Androidu a NSPredicate s parametry v iOS.
API, ke kterému se mobilní aplikace připojuje, je zranitelné stejně jako jakýkoli webový server. Mobilní vývojáři často považují SQLi pouze za problém backendu, ale zranitelnost vzniká na úrovni API endpointu, který přijímá parametry od klienta. Rozdělení odpovědnosti nechrání: pokud vývojář backendu zapomněl parametrizovat dotaz, mobilní aplikace uživatele se stává vektorem útoku. Požadujte od backendu použití ORM nebo prepared statements.
// Příklad SQL Injection v lokálním SQLite na Androidu
// ZRANITELNÝ KÓD: přímé zřetězení
fun getUserVulnerable(db: SQLiteDatabase, userId: String): Cursor {
return db.rawQuery(
"SELECT * FROM users WHERE id = $userId", null
)
}
// BEZPEČNÁ VERZE: parametrizace pomocí selectionArgs
fun getUserSecure(db: SQLiteDatabase, userId: String): Cursor {
return db.rawQuery(
"SELECT * FROM users WHERE id = ?",
arrayOf(userId)
)
}
Ochrana proti SQL Injection je založena na jednoduchém principu: nikdy nevěřte uživatelskému vstupu v SQL dotazech. Jedinou spolehlivou metodou je parametrizace dotazů (prepared statements), při které jsou SQL kód a data přenášeny odděleně. Všechny ostatní metody — escapování, validace, WAF — jsou další vrstvy ochrany, ale nenahrazují parametrizaci. Podle údajů OWASP (2025) parametrizace zabraňuje 100 % útoků SQL Injection.
Při použití prepared statements je SQL dotaz nejprve zkompilován serverem databáze bez dat a poté jsou hodnoty parametrů přenášeny odděleně. Databáze považuje parametry za data, nikoli za spustitelný kód. I když útočník odešle ' OR '1'='1, databáze to interpretuje jako řetězcovou hodnotu, nikoli jako SQL kód. Prepared statements jsou podporovány všemi moderními jazyky a frameworky: PDO v PHP, PreparedStatement v Javě, cursor.execute v Pythonu.
Moderní ORM (Hibernate, Entity Framework, SQLAlchemy, Room) automaticky používají parametrizaci při provádění dotazů, pokud vývojář nepřejde na raw dotazy. Nicméně ORM nechrání zcela: konstrukce jako @Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true) v JPA vyžadují předávání parametrů prostřednictvím pojmenovaných parametrů, nikoli zřetězení. Query Buildery (Knex, jOOQ) také parametrizují dotazy ve výchozím nastavení, pokud se nepoužívají raw metody.
Escapování speciálních znaků (mysql_real_escape_string) — zastaralá metoda, která nechrání proti všem typům SQL Injection. Problém: escapování závisí na kódování a může být obejito pomocí vícebajtových kódování (např. GBK v asijských systémech). Používejte escapování pouze v legacy kódu, kde parametrizace není možná, a vždy v kombinaci s přísnou validací typu vstupních dat.
| Metoda | Účinnost | Doporučení |
|---|---|---|
| Prepared Statements | 100 % | Povinné pro všechny dotazy |
| ORM (správné použití) | 99 % | Doporučuje se |
| Escapování řetězců | 70 % (závisí na kódování) | Pouze legacy |
| Validace vstupu (bílá listina) | 50 % (pouze pro čísla) | Doplňkové |
| WAF (Web Application Firewall) | 60 % | Doplňkové |
Účet aplikace by měl mít minimálně nezbytná oprávnění: SELECT, INSERT, UPDATE, DELETE — pouze na tabulky, které aplikace skutečně potřebuje. Zakážte použití DROP, TRUNCATE, CREATE pro účet aplikace. To omezí škody i v případě úspěšného SQL Injection: útočník nebude moci smazat tabulky nebo provádět administrativní operace.
Pravidelné testování na SQL Injection by mělo být součástí pipelines bezpečného vývoje. Kombinace statické analýzy, dynamického skenování a ručního penetračního testování poskytuje nejlepší výsledky. Podle Synopsys Cybersecurity Report (2025) automatické skenery najdou až 70 % zranitelností SQLi, ale složité Blind útoky vyžadují ruční testování.
Pro mobilní aplikace je důležitá také analýza lokálního SQLite: zkontrolujte všechna volání rawQuery, dotazy ContentProvider a dotazy Room s rawQuery. Nástroje: Android Studio Lint (detekuje SQLi v SQLite), MobSF (Mobile Security Framework) pro automatickou statickou a dynamickou analýzu APK/IPA. Doporučuje se také testování API endpointů pomocí sqlmap s proxy zachytáváním provozu mobilní aplikace.
Často kladené otázky
SQL Injection útočí na relační databáze pomocí SQL dotazů. NoSQL Injection ovlivňuje nerelační databáze (MongoDB, Couchbase) prostřednictvím jejich operátorů dotazů ($gte, $ne, $where). V MongoDB je injekce možná, pokud aplikace vytváří BSON dokument z JSON řetězce. Ochranné mechanismy jsou podobné: parametrizace a validace typů.
Najděte všechna místa, kde jsou SQL dotazy vytvářeny pomocí zřetězení řetězců s uživatelskými daty. Hledejte vzory jako "SELECT ... WHERE id = " + userId nebo f"UPDATE ... SET name = '{name}'". Každý takový řetězec je potenciální SQL injekce. Všechny nahraďte parametrizovanými dotazy nebo prepared statements.
ORM frameworky chrání automaticky pouze tehdy, pokud používáte jejich metody Query Builder a pojmenované parametry. Pokud používáte raw dotazy (nativeQuery v JPA, rawQuery v Room), ochrana nefunguje — parametry musíte předávat prostřednictvím připravených výrazů, nikoli zřetězením řetězců.
Second-Order SQL Injection je útok, při kterém jsou škodlivá data uložena v databázi jako bezpečná, ale poté použita v jiném dotazu bez escapování. Například útočník se zaregistruje s uživatelským jménem ' OR '1'='1. Data jsou uložena jako řetězec — při registraci nedochází k útoku. Pokud ale jiný dotaz dosadí uživatelské jméno do SQL bez parametrizace, injekce se aktivuje.
NoSQL Injection je potenciálně nebezpečnější kvůli nižšímu povědomí vývojářů. Vývojáři vědí o SQL Injection a většina používá ORM, ale o NoSQL Injection ví málokdo. V MongoDB může nesprávně vytvořený dotaz vrátit všechny dokumenty kolekce. Ochrana — stejné prepared statements (BSON parametrizace) a přísná validace vstupních dat.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také