Lekce 2 - Technika útoku SQL injection
V předchozí lekci, Úvod do bezpečnosti webových aplikací, jsme se dozvěděli, co si pod pojmem webová aplikace představit, také jsme si představili princip fungování třívrstvé architektury a také aplikační vrstvu. Známe také Web application firewall, nástroj který chrání aplikační vrstvu aplikace.
V další lekci kurzu bezpečnost webových aplikací se seznámíme s
útokem SQL injection, což je útok, při kterém útočník upraví SQL dotaz
ve svůj prospěch. Nejčastěji se používá u dotazů typu
SELECT
, UPDATE
, INSERT
či
DELETE
a podmínky WHERE
. V příkladech využijeme
databázi MySQL.
SQL injection
SQL injection je typ útoku, který napadá databázovou vrstvu vsunutím (odtud slovo injection) kódu přes neošetřený vstup. S pomocí takto vsunutého kódu může útočník získat citlivé osobní informace, jako například číslo kreditní karty nebo přihlašovací údaje. Také může databázi poškodit smazáním dat, dokonce může databázi upravit ve svůj prospěch. Jak se v dnešní lekci dozvíme, toto nezamyšlené chování vzniká při propojení prezentační a datové vrstvy aplikace.
Útok SQL injection si můžeme rozdělit do několika kategorií:
- Útok využívající spojení více tabulek
- Útok obcházející aplikační logiku
- Útok pozměňující databázi
- Útok, při kterém útočník získá obecné informace o struktuře databáze
Obecně se při SQL injection útocích využívají speciální znaky. Pro SQL databáze jsou to například znaky:
- apostrof / uvozovky - ohraničení řetězce
- pomlčka - dvě pomlčky znamenají komentář (jako např. v PHP dvě lomítka)
- středník - středník znamená konec příkazu
Útok využívající spojení více tabulek
Útok využívá spojení více tabulek v případě, že je výsledek
databázového dotazu vracen přímo do prezentační vrstvy. Jako příklad si
můžeme uvést e-shop. Je pravděpodobné, že bude aplikace náchylná vůči
tomuto typu útoku. Útočník využívá operátor UNION
, který
mu umožní zkombinovat více příkazů typu SELECT
do jediného
výsledku. Mějme tedy e-shop s kategorií slevy mající
následující URL:
https://e-shop.cz/products?category=Slevy
Při navštívení této URL odešle aplikace dotaz do databáze v tomto tvaru:
SELECT * FROM products WHERE category = 'Slevy'
Za použití příkazu UNION
by mohl útočník upravit URL
například takto:
https://e-shop.cz/products?category=Slevy' UNION SELECT username, password FROM users--
Tato upravená URL potom spustí dotaz:
SELECT * FROM products WHERE category = 'Slevy' UNION SELECT username, password FROM users --
Výsledek tohoto dotazu potom zobrazí nejen všechny zlevněné produkty ale
i všechna přihlašovací jména a hesla zákazníků. Přitom, kdyby se za
komentářem --
nacházela nějaká další část dotazu, byla by
vynechána.
Útok obcházející aplikační logiku
Mezi útoky obcházející aplikační logiku můžeme zařadit například takový útok, který obejde přihlašovací formulář. Uveďme si proto konkrétní příklad za použití databáze MySQL a jazyka PHP. Tento příklad použijeme i v dalších příkladech.
Mějme primitivní tabulku s uživateli:
Vytvoříme si formulář, do kterého uživatel zadá své přihlašovací jméno a heslo:
<form method="post"> <table> <tr><th><label for="name">Jméno</th></tr> <tr><td><input type="text" name="name" id="name" /></td></tr> <tr><th><label for="password">Heslo</th></tr> <tr><td><input type="password" name="password" id="password" /></td></tr> <tr><td><input type="submit" value="Přihlásit se" /></td></tr> </table> </form>
Pro představu může formulář vypadat třeba takto:
Zpracování formuláře v PHP může vypadat následovně:
session_start(); $pdo = new PDO("přihlašovací údaje"); $errors = array(); if ($_POST) { if (empty($_POST["name"])) { $errors[] = "Nebylo vyplněno jméno."; } if (empty($_POST["password"])) { $errors[] = "Nebylo vyplněno heslo."; } if (empty($errors)) { $name = $_POST["name"]; $password = hash("SHA512", $_POST["password"] . 'sůůůl'); // Dotaz níže obsahuje nebezpečnou SQL injekci $idQuery = $pdo->query(" SELECT `id` FROM `user` WHERE `name` = '{$name}' AND `password` = '{$password}' LIMIT 1 "); $id = $idQuery->fetchColumn(); if ($id !== FALSE) { $_SESSION["userId"] = $id; header("location:account.php"); exit; } else { $errors[] = "Bylo zadáno špatné jméno nebo heslo."; } } }
Po kliknutí na tlačítko Přihlásit se se odešle do databáze následující dotaz, náchylný k SQL injection útoku:
SELECT `id` FROM `user` WHERE `name` = 'zadaneJmeno' AND `password` = 'hashovaneHeslo' LIMIT 1
Tento dotaz lze jednoduše upravit v útočníkův prospěch. Přidáme již
známé znaky '
a --
do pole pro jméno
následovně:
' OR admin = 1--
Do databáze se odešle tento upravený dotaz:
SELECT `id` FROM `user` WHERE `name` = '' OR admin = 1-- AND `password` = '19c44a96a09dc0088f88d...' LIMIT 1
Díky zadanému apostrofu se ukončí řetězec u sloupce name
a
za ním se vloží podmínka OR admin = 1
. Díky dvěma pomlčkám
se zbytek dotazu považuje za komentář. Upravený dotaz vykoná
následující, vyber ID
z tabulky user
, kde se
jméno rovná prázdnému řetězci nebo admin se rovná jedné. Zbytek dotazu
ignoruj. Dotaz samozřejmě vybere administrátora, za kterého aplikace
útočníka následně přihlásí.
Útok pozměňující databázi
Dotazy typu SELECT
nejsou jediným typem dotazů, pomocí
kterých lze na zranitelnou databázi zaútočit. Dalšími častými dotazy
jsou dotazy typů UPDATE
, INSERT
či
DELETE
. Vycházejme opět z příkladu s přihlašovacím
formulářem. Přidáním řetězce:
'; TRUNCATE TABLE user;--
by útočník mohl tabulku users
smazat. Naopak řetězcem:
' INSERT INTO user (id, name, password, admin) VALUES ('Jan', 'Novák', '123', '1'); --
by útočník mohl do tabulky přidat záznam. S využitím příkazu
UPDATE
by mohl také útočník data pozměnit například pomocí
následujícího řetězce:
'; UPDATE user SET password='noveHeslo' WHERE password='stareHeslo'; --
by útočník mohl změnit hesla uživatelům.
Útok, při kterém útočník získá obecné informace o struktuře databáze
V předešlých příkladech jsme si s jistotou uváděli jména sloupců,
popřípadě tabulek, které chce útočník získat. Někdo by proto mohl
argumentovat, že přece návštěvník nezná název naší databáze, tabulky,
sloupců atd. Jenže 90 % webů má tabulku s uživateli pojmenovanou
buď users
, uzivatele
nebo maximálně
user
a uzivatel
. Útočník má velmi vysokou šanci,
že se trefí. Navíc může útočník využít tohoto typu útoku, který je
využíván pro získání obecných informací o struktuře databáze aplikace.
Tento typ útoku se liší podle typu databáze. Každý typ databáze má
zpravidla také jiný syntaktický zápis. Pro výše zmíněné MySQL existuje
příkaz:
SELECT @@version
jehož výsledkem jsou dodatečné informace o verzi databáze. Výstup tohoto příkazu může vypadat třeba takto:
+-------------------------+ | @@version | +-------------------------+ | 5.7.16-0ubuntu0.16.04.1 | +-------------------------+ 1 row in set (0.00 sec)
Tento příkaz lze opět velmi snadno použít v aplikaci, která není proti SQL injection útoku chráněna, například nám již známým způsobem:
' UNION SELECT @@version--
Výsledný dotaz potom bude vypadat následovně:
SELECT `id` FROM `user` WHERE `name` = '' UNION SELECT @@version-- AND `password` = '19c44a96a09dc0088f88d...' LIMIT 1
Když útočník ví, jaká verze databáze běží na datovém serveru.
Poté je velmi snadné dohledat příkazy, které informují o jednotlivých
tabulkách. Pro MySQL je to například příkaz
SELECT * FROM INFORMATION_SCHEMA.TABLES
, který vrací seznam
tabulek v databázi. Jehož výstup může vypadat následovně:
|------------------------------------------------------------------------ | TABLE_CATALOG | TABLE_SCHEMA | TABLE_NAME | TABLE_TYPE | |------------------------------------------------------------------------ | MyDatabase | dbo | user | BASE TABLE |
Z výsledku jde jasně vyčíst, že v databázi je tabulka
user
. S touto informací může útočník dále pracovat a
použít například příkaz
SELECT * FROM information_schema.columns WHERE table_name = 'user'
,
který vypíše informace o konkrétní tabulce:
|------------------------------------------------------------------------------------------ | TABLE_CATALOG | TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME | DATA_TYPE | |------------------------------------------------------------------------------------------ | MyDatabase | dbo | user | id | int | | MyDatabase | dbo | user | name | varchar | | MyDatabase | dbo | user | Password | char |
Z výsledku můžeme vidět, které sloupce jakých datových typů tabulka obsahuje. To by pro dnešní lekci bylo vše
V další lekci, Jak se bránit proti SQL injection, si uvedeme způsoby, jak před útokem SQL injection svou aplikaci chránit.