2. diel - Technika útoku SQL injection
V predchádzajúcej lekcii, Úvod do bezpečnosti webových aplikácií , sme sa dozvedeli, čo si pod pojmom webová aplikácia predstaviť, tiež sme si predstavili princíp fungovania trojvrstvovej architektúry a tiež aplikačnú vrstvu. Poznáme tiež Web application firewall, nástroj ktorý chráni aplikačnú vrstvu aplikácie.
V ďalšej lekcii kurze bezpečnosť webových aplikácií sa zoznámime s
útokom SQL injection, čo je útok, pri ktorom útočník upraví SQL dotaz vo
svoj prospech. Najčastejšie sa používa u otázok typu SELECT
,
UPDATE
, INSERT
či DELETE
a podmienky
WHERE
. V príkladoch využijeme databáze MySQL.
SQL injection
SQL injection je typ útoku, ktorý napáda databázovú vrstvu vsunutím (odtiaľ slovo injection) kódu cez neošetrený vstup. S pomocou takto vsunutého kódu môže útočník získať citlivé osobné informácie, ako napríklad číslo kreditnej karty alebo prihlasovacie údaje. Tiež môže databázu poškodiť zmazaním dát, dokonca môže databázu upraviť vo svoj prospech. Ako sa v dnešnej lekcii dozvieme, toto nezamýšľané správanie vzniká pri prepojení prezentačnej a dátovej vrstvy aplikácie.
Útok SQL injection si môžeme rozdeliť do niekoľkých kategórií:
- Útok využívajúca spojenie viacerých tabuliek
- Útok obchádzajúce aplikačnú logiku
- Útok pozmeňujúci databázu
- Útok, pri ktorom útočník získa všeobecné informácie o štruktúre databázy
Všeobecne sa pri SQL injection útokoch využívajú špeciálne znaky. Pre SQL databázy sú to napríklad znaky:
- apostrof / úvodzovky - ohraničenie reťazca
- pomlčka - dve pomlčky znamenajú komentár (ako napr. v PHP dve lomítka)
- bodkočiarka - bodkočiarka znamená koniec príkazu
Útok využívajúca spojenie viacerých tabuliek
Útok využíva spojenie viacerých tabuliek v prípade, že je výsledok
databázového dopytu vracané priamo do prezentačnej vrstvy. Ako príklad si
môžeme uviesť e-shop. Je pravdepodobné, že bude aplikácia náchylná voči
tomuto typu útoku. Útočník využíva operátor UNION
, ktorý mu
umožní skombinovať viac príkazov typu SELECT
do jediného
výsledku. Majme teda e-shop s kategóriou zľavy majúci
nasledujúce URL:
https://e-shop.cz/products?category=Slevy
Pri navštívení tejto URL odošle aplikácie dopyt do databázy v tomto tvare:
SELECT * FROM products WHERE category = 'Slevy'
Za použitia príkazu UNION
by mohol útočník upraviť URL
napríklad takto:
https://e-shop.cz/products?category=Slevy' UNION SELECT username, password FROM users--
Táto upravená URL potom spustí dotaz:
SELECT * FROM products WHERE category = 'Slevy' UNION SELECT username, password FROM users --
Výsledok tohto dotazu potom zobrazia nielen všetky zľavnené produkty ale
aj všetky prihlasovacie mená a heslá zákazníkov. Pritom, keby sa za
komentárom --
nachádzala nejaká ďalšia časť dopytu, bola by
vynechaná.
Útok obchádzajúce aplikačnú logiku
Medzi útoky obchádzajúce aplikačnú logiku môžeme zaradiť napríklad taký útok, ktorý obíde prihlasovací formulár. Uveďme si preto konkrétny príklad za použitia databázy MySQL a jazyka PHP. Tento príklad použijeme aj v ďalších príkladoch.
Majme primitívne tabuľku s používateľmi:
Vytvoríme si formulár, do ktorého používateľ zadá svoje prihlasovacie meno 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>
Pre predstavu môže formulár vyzerať napríklad takto:
Spracovanie formuláre v PHP môže vyzerať nasledovne:
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čidlo Prihlásiť sa sa odošle do databázy nasledujúce dotaz, náchylný k SQL injection útoku:
SELECT `id` FROM `user` WHERE `name` = 'zadaneJmeno' AND `password` = 'hashovaneHeslo' LIMIT 1
Tento dotaz možno jednoducho upraviť v útočníkov prospech. Pridáme už
známe znaky '
a --
do poľa pre meno nasledovne:
' OR admin = 1--
Do databázy sa odošle tento upravený otázka:
SELECT `id` FROM `user` WHERE `name` = '' OR admin = 1-- AND `password` = '19c44a96a09dc0088f88d...' LIMIT 1
Vďaka zadanému apostrofu sa ukončí reťazec u stĺpce name
a
za ním sa vloží podmienka OR admin = 1
. Vďaka dvom pomlčkám
sa zvyšok dopytu považuje za komentár. Upravený dotaz vykoná nasledujúce,
vyber ID
z tabuľky user
, kde sa meno rovná
prázdnemu reťazci alebo admin sa rovná jednej. Zvyšok dotazu ignoruj.
Otázka samozrejme vyberie administrátora, za ktorého aplikácia útočníka
následne prihlási.
Útok pozmeňujúci databázu
Otázky typu SELECT
nie sú jediným typom otázok, pomocou
ktorých možno na zraniteľnú databázu zaútočiť. Ďalšími častými
otázky sú otázky typov UPDATE
, INSERT
či
DELETE
. Vychádzajme opäť z príkladu s prihlasovacím
formulárom. Pridaním reťazca:
'; TRUNCATE TABLE user;--
by útočník mohol tabuľku users
zmazať. Naopak
reťazcom:
' INSERT INTO user (id, name, password, admin) VALUES ('Jan', 'Novák', '123', '1'); --
by útočník mohol do tabuľky pridať záznam. S využitím príkazu
UPDATE
by mohol tiež útočník dáta pozmeniť napríklad pomocou
nasledujúceho reťazca:
'; UPDATE user SET password='noveHeslo' WHERE password='stareHeslo'; --
by útočník mohol zmeniť heslá používateľom.
Útok, pri ktorom útočník získa všeobecné informácie o štruktúre databázy
V predošlých príkladoch sme si s istotou uvádzali mená stĺpcov,
prípadne tabuliek, ktoré chce útočník získať. Niekto by preto mohol
argumentovať, že predsa návštevník nepozná názov našej databázy,
tabuľky, stĺpcov atď. Lenže 90% webov má tabuľku s používateľmi
pomenovanú buď users
, uzivatele
alebo
maximálne user
a uzivatel
. Útočník má veľmi
vysokú šancu, že sa trafí. Navyše môže útočník využiť tohto typu
útoku, ktorý je využívaný pre získanie všeobecných informácií o
štruktúre databázy aplikácie. Tento typ útoku sa líši podľa typu
databázy. Každý typ databázy má spravidla tiež iný syntaktický zápis.
Pre vyššie zmienené MySQL existuje príkaz:
SELECT @@version
ktorého výsledkom sú dodatočné informácie o verzii databázy. Výstup tohto príkazu môže vyzerať napríklad takto:
+-------------------------+ | @@version | +-------------------------+ | 5.7.16-0ubuntu0.16.04.1 | +-------------------------+ 1 row in set (0.00 sec)
Tento príkaz možno opäť veľmi ľahko použiť v aplikácii, ktorá nie je proti SQL injection útoku chránená, napríklad nám už známym spôsobom:
' UNION SELECT @@version--
Výsledný dotaz potom bude vyzerať nasledovne:
SELECT `id` FROM `user` WHERE `name` = '' UNION SELECT @@version-- AND `password` = '19c44a96a09dc0088f88d...' LIMIT 1
Keď útočník vie, aká verzia databázy beží na dátovom serveri. Potom
je veľmi jednoduché dohľadať príkazy, ktoré informujú o jednotlivých
tabuľkách. Pre MySQL je to napríklad príkaz
SELECT * FROM INFORMATION_SCHEMA.TABLES
, ktorý vracia zoznam
tabuliek v databáze. Ktorého výstup môže vyzerať nasledovne:
|------------------------------------------------------------------------ | TABLE_CATALOG | TABLE_SCHEMA | TABLE_NAME | TABLE_TYPE | |------------------------------------------------------------------------ | MyDatabase | dbo | user | BASE TABLE |
Z výsledku ide jasne vyčítať, že v databáze je tabuľka
user
. S touto informácií môže útočník ďalej pracovať a
použiť napríklad príkaz
SELECT * FROM information_schema.columns WHERE table_name = 'user'
,
ktorý vypíše informácie o konkrétnej tabuľke:
|------------------------------------------------------------------------------------------ | 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 vidieť, ktoré stĺpce akých dátových typov tabuľka obsahuje. To by pre dnešné lekciu bolo všetko:)
V ďalšej lekcii, Ako sa brániť proti SQL injection , si uvedieme spôsoby, ako pred útokom SQL injection svoju aplikáciu chrániť.