Na manipuláciu s dátami uloženými v databáze sa používajú SQL dotazy. Jazyk SQL sa delí na dve hlavné kapitoly: na DDL (Data Definition Language) a DML (Data Modification Language).

Medzi príkazy typu DDL, čiže tie, ktoré slúžia na definíciu dát, spadajú príkazy CREATE, DROP, ALTER a príkazy na modifikáciu dát, DML, sú SELECT, INSERT, UPDATE, DELETE.

Typický SQL dotaz má tvar:
  • SELECT A1, A2, ..., An FROM r1, r2, ...., rn WHERE P
kde Ai reprezentuje atribúty záznamov uložených v tabuľkách, ri reprezentuje konkrétne tabuľky a P je predikát, ktorým možno filtrovať výsledky.



Príklad tabuľky v databáze.

SQL príkaz CREATE TABLE slúži na vytvorenie novej tabuľky v databáze. Prvý parameter je názov novej tabuľky a v zátvorke sú definované atribúty tabuľky, ktorým je možné špecifikovať mená, typy a obmedzenia integrity. Nasledujúci dotaz vytvorí tabuľku zákazník, ktorá má atribúty id typu integer, ktorý je zároveň primárnym kľúčom , meno, priezvisko a bydlisko, ktoré sú typu varchar s rôznou maximálnou dĺžkou.

  • CREATE TABLE zákazník
  • (id integer NOT NULL AUTO_INCREMENT ,
  • meno varchar(20),
  • priezvisko varchar(30),
  • bydlisko varchar(255),
  • PRIMARY KEY (id));
Pre zmazanie celej tabuľky vrátane jej obsahu slúži príkaz DROP. V ukážke bude zmazaná celá tabuľka zákazník.
  • DROP TABLE zákazník;
Pomocou príkazu ALTER môžeme v už existujúcej tabuľke pridávať, mazať alebo meniť stĺpce (atribúty). Uvažujme existujúcu reláciu zákazník (viď príkaz CREATE):

  • pre pridanie atribútu stav účtu typu integer použijeme kľúčové slovo ADD ALTER TABLE zákazník
  • pre opätovné odobratie atribútu sa používajú kľúčové slová DROP COLUMN
ALTER TABLE zákazník
DROP COLUMN stav_účtu;

  • na zmenu typu atribútu slúži CHANGE
ALTER TABLE zákazník
CHANGE bydlisko bydlisko VARCHAR(50);


Najpoužívanejší príkaz na prácu s dátami v databáze je príkaz SELECT, ktorý vracia množinu záznamov podľa daných podmienok. Nasledujúci dotaz vráti mená a priezviská všetkých zákazníkov z tabuľky zákazník, ktorí bývajú v Brne:

SELECT meno, priezvisko
FROM zákazník
WHERE bydlisko = "Brno";

Ďalší príkaz slúžiaci na zmenu dát v tabuľke je príkaz INSERT, ktorý slúži na vloženie nového záznamu do tabuľky. Vloženie nového zákazníka s menom John Doe a bydliskom Colorado vyzerá:

  • INSERT INTO zákazník
  • (meno, priezvisko, bydlisko)
  • VALUES
  • (´John´, ´Doe´, ´Colorado´);
Príkaz UPDATE umožňuje vykonať zmenu v záznamoch v tabuľke. V príklade je zmenené bydlisko zákazníka John Doe na Miami:

  • UPDATE zákazník
  • SET bydlisko = ´Miami´
  • WHERE meno = ´John´ AND priezvisko = ´Doe´;
Pre zmazanie záznamu z tabuľky slúži príkaz DELETE. Nasledujúca ukážka zmaže zákazníka John Doe:

  • DELETE FROM zákazník
  • WHERE meno = ´John´ AND priezvisko = ´Doe´;

Samotný SQL Injection útok

SQL Injection je technika vloženia kódu, ktorý využíva bezpečnostné nedostatky v databázach. Ide o veľmi častý typ útoku, kvôli jeho relatívne ľahkej uskutočniteľnosti a hlavne kvôli atraktivite dát, ktoré sú uchovávané v databázach. Webové služby často používajú SQL dotazy na získavanie dát z databázy na základe vstupu od užívateľa. Ak toto prehľadávanie záznamov nie je riešené s ohľadom na bezpečnosť, tak to umožňuje útočníkovi prístup k databáze, kde môže ľubovoľne čítať alebo meniť dáta, alebo dokonca môže získať až prístup k serveru, na ktorom je databáza umiestnená. Najčastejšie ide o snahu útočníka o vloženie podvrhnutých vstupných dát tak, aby bol nejakým spôsobom zmenený výsledok SQL dotazu. Zraniteľnosť sa objaví, keď je užívateľský vstup buď vôbec, alebo nekorektne filtrovaný od riadiacich znakov SQL príkazov, alebo nie je silne typovaný a tým pádom je vykonaný škodlivý kód.


Nesprávna alebo žiadna filtrácia riadiacich znakov

Tento typ SQL Injection útoku je jedným z najzákladnejších a najľahšie brániteľných útokov. SQL dotazy sú často vytvárané priamo z užívateľských vstupov bez filtrácie, čo umožňuje jednoduchú zmenu SQL dotazu napríklad použitím symbolu apostrofu. Uvažujme fiktívne internetové kníhkupectvo s možnosťou vyhľadávať knihy podľa autora.


T
Zoznam kníh kníhkupectva.

SQL dotaz pre vyhľadanie všetkých kníh od Umberta Eca vyzerá:
  • SELECT názov, rok_vydania
  • FROM knihy
  • WHERE meno_autora=´Umberto´ AND priezvisko_autora=´Eco´;
V tomto prípade prebehne dotaz bez problémov a skutočne sa vrátia všetky knihy od Umberta Eca. Keď však chceme vyhľadať knihy od Edny O´Brien, narazíme na problém s filtrovaním znakov. V tomto prípade vyzerá dotaz nasledovne:

  • SELECT názov, rok_vydania
  • FROM knihy
  • WHERE priezvisko_autora = ´O´Brien´
Pri spracovávaní tohto dotazu interpreter dotazov rozozná užívateľský vstup ako "O" a snaží sa vyhodnotiť výraz Brien, čo nie je výraz v jazyku SQL a tak celý SQL dotaz skončí s chybou. Týmto spôsobom môže útočník zistiť, že vstup nie je filtrovaný a s dostatkom znalostí môže vkladať vlastné dotazy. Pre výpis celej tabuľky stačí správne ukončiť pôvodný dotaz znakom apostrofu a pridať logickú podmienku, ktorá je vždy pravdivá a prípadne znak komentáru, pre odfiltrovanie ďalších podmienok v dotaze. Pri zadaní reťazca Eco´ OR 1=1-- sa celý dotaz vyhodnotí na

  • SELECT názov, rok_vydania
  • FROM knihy
  • WHERE priezvisko_autora = ´Eco´ OR 1=1--´
Čo je syntakticky správny dotaz, pretože posledný znak apostrofu je odfiltrovaný. Tento dotaz prechádza všetky záznamy v tabuľke a vypisuje tie, kde je priezvisko autora Eco alebo kde sa 1 rovná 1. Keďže 1 sa rovná 1 vždy, vypíšu sa všetky riadky tabuľky, pokiaľ nie výpis výsledku implementovaný tak, že sa má vypísať len prvý prvok.

Blind SQL Injection

Pri pokuse útočníka zistiť, či je server chránený proti SQL Injection, pomáha aj chybová hláška zobrazovaná pri chybnej syntaxi SQL dotazu. Blind SQL Injection je typovo rovnaký útok ako SQL Injection, ale s tým rozdielom, že namiesto toho, aby útočník dostal chybovú hlášku pri chybnom dotaze, dostane len chybovú stránku definovanú programátorom. V prípade použitia databázy MySQL môže chybová hláška vyzerať nasledovne:

  • Warning: mysql_fetch_assoc() expects parameter 1 to be resource, boolean given in /var/www/index.php on line 7
Slepý útok na databázu môže byť podniknutý tromi spôsobmi:
  • Útok použitím podmienenej reakcie,
  • Útok prostredníctvom podmienenej chyby,
  • Útok použitím časového oneskorenia.
Pri útoku použitím podmienenej reakcie sa snaží útočník vykonať SQL Injection pomocou pridávania logických hodnôt do dotazov. Ak sa stránka chová rôzne v závislosti na pravdivosti a nepravdivosti výrazu, je server náchylný k SQL Injection.

Ako príklad môžeme uviesť napríklad získavanie článkov z databázy serveru. Bežný dotaz má nasledovnú podobu:
  • http://www.example.com/news.php?articleId=1
Následne sa útočník pokúsi vložiť logické podmienky, z ktorých je jedna pravdivá a jedna nepravdivá:
  • http://www.example.com/news.php?articleId=1+AND+1=1
  • http://www.example.com/news.php?articleId=1+AND+1=0
Ak tieto dva dotazy vrátia rôzny výsledok, je táto stránka náchylná voči tomuto typu útoku.



Útok prostredníctvom podmienenej chyby znamená, že útočník vloží do dotazu výraz, ktorý je syntakticky správny, ale jeho spracovanie vyvolá inú chybu. V ukážke je použité delenie nulou, ktoré je vyhodnotené až v prípade, keď je výsledok príkazu WHERE pravdivý. Daný príklad umožňuje útočníkovi zisťovať podľa chyby pri delení nulou, či daný užívateľ v databáze existuje.
  • SELECT 1/0
  • FROM zákazník
  • WHERE login = ´root´;
Útok použitím časového oneskorenia je založený na zmerateľnom oneskorení odpovede v závislosti na dlhom vyhodnotení príkazu po teste podmienky. K tomuto účelu je používaný príkaz SELECT v kombinácii s príkazom IF(logický výraz, true vetva, else vetva), do ktorého sa vloží logický výraz, ktorý chce útočník zistiť a do jednej vetvy sa vloží operácia, ktorej vyhodnotenie trvá istú dobu, ktorú je možno zmerať.
  • SELECT IF(SUBSTRING(názov,1,1) = CHAR(86), BENCHMARK(5000000,ENCODE(´TIME´,´TEST´)),null)
  • FROM knihy
  • WHERE rok_vydania = 1936;
Týmto typom dotazu môže útočník ľahko zisťovať bližšie informácie o údajoch uložených v databáze. Ak je podmienka pravdivá ? v tomto prípade ak sa prvé písmeno knihy vydanej v roku 1936 rovná písmenu "V", je príkaz ENCODE vykonaný 5000000-krát, čo spôsobí merateľné časové zdržanie a naznačí útočníkovi, že podmienka bola pravdivá.

Typy hrozieb spojené so SQL Injection

Prístup neautorizovaného útočníka k databáze môže znamenať veľké problémy pre webovú službu. Útokom SQL Injection môže útočník urobiť 4 základné veci:

  • Môže získať prístup k citlivým dátam (napríklad užívateľské heslá, skryté e-maily, ...),
  • môže získať prístup k akémukoľvek účtu, napríklad administrátorskému a tým prevziať kontrolu nad celou aplikáciou,
  • môže získať prístup ku všetkým účtom naraz,
  • môže zmazať všetky údaje uložené v tabuľkách v databáze.

information_schema

Pri útokoch na databázy útočník vo väčšine prípadov nepozná presné názvy tabuliek a stĺpcov, ktoré sa v nich nachádzajú, čo mu komplikuje útok. Information_schema je informačná databáza, ktorá je určená len na čítanie dát a uchováva informácie o všetkých ostatných databázach, ktoré sa nachádzajú na serveri. Táto databáza je prítomná vo viacerých typoch databáz, napríklad MySQL, Oracle alebo PostgreSQL. Pri útoku pomocou information_schema je možné zaútočiť napríklad pomocou upraveného URL linku. Ako prvý krok musí útočník zistiť, či je daný link zneužiteľný na útok a to môže zistiť pomocou dvoch nasledujúcich dotazov:
  • www.example.com/news.php?articleId=5+AND+1=0--
  • www.example.com/news.php?articleId=5+AND+1=1--
Prvý dotaz povie serveru, že má zobraziť článok s ID=5, ak platí, že 1=0. Druhý dotaz má zase zobraziť ten istý článok ak 1=1. Ak sa výstupy týchto dvoch príkazov líšia, čiže pri prvom dotaze nebol vypísaný daný článok, znamená to, že aplikácia je náchylná voči tomuto typu útoku.


Ako ďalšie v poradí treba zistiť, koľko stĺpcov sa vypisuje na stránke. To zistíme postupným skúšaním dotazov s klauzulou ORDER BY, ktorá radí výsledky podľa daného stĺpca, ktorého index je zadaný za ňou.
  • www.example.com/news.php?articleId=5+ORDER+BY+1--
Ak tento príkaz neskončí s chybovou hláškou, útočník zvýši číslo stĺpcov, až kým server nevráti chybovú hlášku. Pre príklad predpokladajme, že stránka obsahuje 3 stĺpce. Útočník môže získať mená všetkých tabuliek zadaním nasledujúceho reťazca ako URL - keďže sme zistili, že aplikácia obsahuje 3 stĺpce, jeden z nich nahradíme hodnotou mien tabuliek z databázy:

  • www.example.com/news.php?articleId=-1+UNION+SELECT+1,table_name,3
+FROM+INFORMATION_SCHEMA.TABLES--

Ak je stránka konštruovaná tak, že vypisuje vždy len prvý nájdený prvok, potom je možné vypisovať ďalšie prvky pomocou SQL príkazu LIMIT X,Y, kde X je pozícia, od ktorej sa má začať vypisovať, pričom prvý index je 0, a Y je počet prvkov, ktoré majú byť vypísané. Pre príklad môžeme uvažovať, že sme našli tabuľku s názvom Users a chceme si zobraziť druhý a tretí stĺpec. To dosiahneme nasledujúcim príkazom:

  • www.example.com/news.php?articleId=-1+UNION+SELECT+1,column_name,
3+FROM+INFORMATION_SCHEMA.COLUMNS+WHERE+
table_name= ´Users´ LIMIT 1,2--

Predpokladajme, že sme našli stĺpce username a password. Prvý záznam zo stĺpca username si zobrazíme touto URL (predpokladáme, že zobrazovanie článkov na stránke je nastavené na zobrazovanie maximálne jedného článku):

  • www.example.com/news.php?articleId=-1+UNION+SELECT+1,username,3+FROM+
Users--

Výsledok tohto príkazu nám vráti užívateľské meno prvého užívateľa uloženého v databáze. Uvažujme, že tento užívateľ má meno admin a chceme získať jeho heslo. Skonštruujeme preto nasledujúcu URL, ktorá namiesto článku vypíše heslo užívateľa admin:

  • www.example.com/news.php?articleId=-1+UNION+SELECT+1,password,3+FROM
+USERS+WHERE+username=?admin?--

Automatizované frameworky na hľadanie zraniteľností

Nájdenie zraniteľnosti vo webovej aplikácii nie je jednoduchou záležitosťou. Každá webová aplikácia je špecifická a preto neexistuje univerzálny spôsob na zistenie zraniteľnosti. Na internete je dostupné veľké množstvo automatizovaných nástrojov na hľadanie zraniteľností SQL Injection vo webovej aplikácii. Ako príklad takéhoto nástroja môžeme uviesť aplikáciu sqlmap , ktorá je naprogramovaná v jazyku Python dostupná ako open source. Tento nástroj pracuje v textovom režime a je schopný odhaliť 5 typov SQL Injection: blind SQL Injection pomocou logickej podmienky, blind SQL Injection pomocou časového oneskorenia, SQL Injection založeného na chybových hláškach, spájanie tabuliek pomocou príkazu UNION a posledným typom je vykonávanie viacerých SQL príkazov v jednom dotaze, ktoré sú oddelené znakom bodkočiarky. Medzi ďalšie podobné aplikácie môžeme zaradiť napríklad BSQL Hacker alebo Absinthe .


Proces zisťovania názvov stĺpcov tabuliek pomocou nástroja sqlmap

Obrana proti SQL Injection

Na obranu proti útokom typu SQL Injection existuje viacero rôznych spôsobov, všetky z nich vychádzajú z princípu defenzívneho programovania, pri ktorom musí programátor počítať s tým, že užívateľský vstup predstavuje potenciálne nebezpečenstvo pre webovú aplikáciu.

Filtrácia riadiacich znakov

Jeden z najzákladnejších spôsobov obrany proti SQL Injection je korektne odfiltrovať riadiace znaky jazyka SQL. MySQL podporuje 2 módy filtrovania, ANSI SQL mód, ktorý nahradzuje všetky znaky apostrofov z užívateľského vstupu za znaky úvodzoviek, alebo MySQL mód, ktorý mení riadiace znaky na predom dané sekvencie znakov. Pre tento účel sú v jazyku PHP v kombinácii s MySQL implementované funkcie, ktoré dané riadiace znaky odfiltrujú. Funkcia mysql_real_escape_string funguje na základe vloženia znaku ?\? pred každý riadiaci znak, čím znemožňuje vkladanie vlastných dotazov.
  • $username = " ´ OR 1=1´"
  • $username = mysql_real_escape_string($username);
Pri takomto použití funkcia zmení užívateľský vstup ´ OR 1=1´ na \´ OR 1=1\´, čo následne spôsobí syntaktickú chybu pri vykonávaní dotazu.

S filtrovaním súvisí aj takzvaný white-listing. Je to technika, ktorá jasne vymedzuje (napríklad pomocou regulárneho výrazu), ktoré znaky je možné použiť. Hlavnou výhodou tohto prístupu je, že ak vstup obsahuje aspoň jeden nepovolený znak, alebo je jeho dĺžka väčšia ako maximálne prípustná, je celý vstup ihneď odmietnutý.

[a-zA-Z0-9 ]{1-50}

Daný regulárny výraz umožňuje užívateľovi zadať alfanumerické znaky a znak medzery, avšak výsledný text musí mať dĺžku v rozpätí 1 až 50 znakov.

Parametrizované dotazy

Ďalšou z metód obrany sú takzvané parametrizované alebo pripravené (prepared) dotazy . Tieto dotazy umožňujú oddeliť štruktúru dotazu od samotných dát použitých v dotazoch. Pomocou zástupných symbolov je dotaz definovaný len raz a je možné ho vykonávať viackrát s rôznymi parametrami. Pri vykonávaní tohto dotazu sa nahrádza zástupný symbol priamo daným parametrom. Samotná definícia dotazu je rozdelená do dvoch častí:

  • Aplikácia špecifikuje štruktúru dotazu a pre každú položku užívateľského vstupu použije jeden zástupný symbol.
  • Aplikácia nahradí každý zástupný znak jednou položkou užívateľského vstupu.
String query = " SELECT názov, rok_vydania FROM knihy WHERE nazov = ?" ;
statement = con.prepareStatement(query);
statement.setString(1, request.getParameter("nazov"));
result = statement.executeQuery();

V danom príklade, ktorý je v jazyku Java, je najskôr pripravená štruktúra celého dotazu s použitím jedného zástupného znaku a o escapovanie sa stará v prípade Javy JDBC databázový driver. Následne je táto štruktúra uložená do premennej a v ďalšom kroku sa pripraví samotný dotaz pomocou premennej con, pomocou ktorej je realizované spojenie s databázou. Následne sa namiesto zástupného znaku doplní údaj z užívateľského vstupu a nakoniec je celý dotaz vykonaný a uložený do premennej result.


Princíp najmenších práv

Princíp najmenších práv sa používa ako dodatočná forma ochrany webovej služby. Neslúži len ako ochrana proti SQL Injection útokom, ale aj ako ochrana prístupu k dátam, ku ktorým by užívateľ nemal mať prístup. Vždy by malo platiť, že každá aplikácia má len také práva, ktoré nevyhnutne potrebuje k správnemu fungovaniu. Ako správny prístup sa javí postupovať od najmenších práv a postupne práva pridávať. Taktiež je dôležité, aby daná aplikácia nebola spustená pod administrátorskými právami v operačnom systéme.

Použitie frameworkov na ochranu aplikácie

Používanie frameworkov môže uľahčiť programátorom tvorbu webových aplikácií. Veľa frameworkov pre jazyk PHP v sebe obsahuje už implementované funkcie pre validáciu a filtráciu vstupných reťazcov. V prípade frameworku Zend ide o rozhranie Zend_Filter_Input, z názvu ktorého vidno, že sa stará o filtrovanie vstupu. Toto rozhranie najskôr uzavrie všetky dáta zo vstupu do klietky a následne aplikácia získava dáta tak, že oboznámi klietku, ako majú dané dáta vyzerať a ako ich aplikácia plánuje použiť. Ak je potrebné, tak je použitá funkcia escape(), ktorá je založená na PHP htmlspecialchars() funkcii, čo sa dá ale zmeniť pomocou funkcie setEscape(). Ak dáta spĺňajú všetky náležitosti, sú uvoľnené z klietky a predané aplikácii.


Autor: Bc. Andrej Vaňo