SQL Injection - čo to je a ako sa proti tomu brániť?

Publikované 19.2.2014 16:48   |  PHP, Security 1

S pojmom SQL Injection ste sa zrejme už stretli. Ak nie, je na čase sa s ním stretnúť, lebo neznalosť tohto problému je to, že náš PHP skript, ktorý využíva prácu s databázou bude skákať tak, ako útočník píska.


Je to jedna z najhlávnejších prvkov, ktorá nás núti si náš sýstem proti tomuto chrániť. Ak proti tomuto nemáme systém chránený, ďalšie ochrany sú zbytočné, keďže sme nechali doslova otvorenú bránu ľubovoľnému útočníkovi si robiť s obsahom, čo sa mu zachce.

Čo je vlastne to SQL Injection

Z informatického pojmu pojem Injection znamená, že vkládame niečo pomocou napr. parametra. Tak ako máme v OOP - inject-ujeme určitú inštanciu triedy pomocou toho, že do najčastejšie konštruktora pridáme nejaký parameter typu danej triedy, ktorý si potom už v triede uložíme a následne z danou inštanciou pracujeme.

SQL Injection funguje na rovnakom príncipe. Len to nevkládame my, ale vkláda do návštevník, teda útočník, ktorý sa pokúša takto dostať k citlivým informáciam, alebo priamo poškodiť naše dáta. Útočník na danom skripte sa pokúša, či už pomocou formulára (POST) alebo URL adresy (GET) posielať údaje, ktoré sú zrejmé, že budú využité na nejaký SQL dotaz. Jednoduchý príklad:

// ...

$id = $_GET['id'];

$query = mysqli_query($conn, "SELECT * FROM uzivatelia WHERE id = '" . $id . "'");
$riadok = mysqli_fetch_assoc($query);

$meno = $riadok['meno'];

// ...

Ak takýmto spôsobom píšete skripty, bezpodmienečne pokračujte v čítaní. Takto očakávame, že ak zadáme do URL napr:

/uzivatelia.php?id=3

Tak nám vypíše meno o užívateľovi, ktorý má ID 3. Čo ak by používateľ meno vedel a chcel získať aj heslo? Myslíte si, že to nie je možné? Tak sa pozerajte, troška teraz danú adresu upravíme:

/uzivatelia.php?id='; SELECT heslo AS meno FROM uzivatelia WHERE id = '1

Aký bude teda výsledný SQL skript?

SELECT * FROM uzivatelia WHERE id = ''; SELECT heslo AS meno FROM uzivatelia WHERE id = '1'

Čo to teda znamená? Asi nič pekné a máte pravdu. Namiesto toho, aby si dal vypísať meno používateľa si takýmto spôsobom vypísal heslo a mohol ho zneužiť vo svoj prospech (ešte horšie, ak heslo sa neuložilo do databázy ako zašifrované). Alebo ak vedel ako sa heslo šifruje, mohol rovno bez odhaľovania hesla priamo vykonať UPDATE dotaz a zmenil by heslo podľa svojho uváženia. Pri najhoršom by pomenil heslá všetkým (zmenil každému šifru napr. aj na nejakú hlúposť) a mohlo by to spôsobiť obrosvké škody, zvlášť ak by nebola vykonaná záloha používateľov.

Je o tom veľa čo písať, čo by mohol takýmto spôsobom spraviť.

Ako tomuto zabrániť?

Jednou z najúčinejších metód je escapovanie (sú aj miesta, kde escapovanie nestačí, ale to neskôr). Niektorí si možno poviete: Ahá, použijem addslashes(). Ani náhodou! Funkcia addslashes() nestačí na ochranu proti SQL Injection. PHP obsahuje špeciálnu funkciu na escapovanie znakov pre SQL dotazy a ňou je mysqli_real_escape_string(). Táto funkcia očakáva 2 parametre:

  • odkaz (link) na MySQLi pripojenie
  • samotný string, ktorý sa má escapovať

Takže ako to má vyzerať na príklade? Následovne:

// ...

$id = $_GET['id'];

$query = mysqli_query($conn, "SELECT * FROM uzivatelia WHERE id = '" . mysqli_real_escape_string($conn, $id) . "'");
$riadok = mysqli_fetch_assoc($query);

$meno = $riadok['meno'];

// ...

Čo sa stane, ak teraz zadáme tento škodlivý kód do adresy? Táto funkcia to opraví tak, že už daný string neutečie spomedzi apostrofov:

SELECT * FROM uzivatelia WHERE id = '\'; SELECT heslo AS meno FROM uzivatelia WHERE id = \'1'

Teda, tento dotaz vráti 0 výsledkov.

Samozrejme, že čísla, teda tam kde očakávame typ integer (ako napr. id) môžeme namiesto funkcie použiť len (int) pred číslom. Vyzeralo by to takto:

// ...

$id = $_GET['id'];

$query = mysqli_query($conn, "SELECT * FROM uzivatelia WHERE id = '" . (int) $id . "'");
$riadok = mysqli_fetch_assoc($query);

$meno = $riadok['meno'];

// ...

Znak (int) hovorí, že sa pokúsi daný typ obsahu previesť na číslo. Ak sa mu to nebude môcť podariť, vráti 0 - každý SQL Injection útok sa prekonvertuje na toto číslo.

Ale v určitých prípadoch escapovanie nestačí!

A týka sa to takých prípadoch, kde využívať z URL data napr. na zoradenie, vybranie N položiek, proste všade tam, kde už apostrofy byť nemôžu. Napr. tu:

$query = mysqli_query($conn, "SELECT * FROM uzivatelia WHERE id = '" . (int) $id . "' LIMIT " . mysqli_real_escape_string($conn, $_GET['limit']));

Takýmto spôsobom ale používateľ nepotrebuje zadávať žiadne apostrofy do URL adresy a jednoducho pomocou medzery si daný SQL script upraví podľa uváženia používateľa. Riešením je prevádzať na čísla všetko to, čo 100% má byť číslo (napr. LIMIT cislo). V ORDER BY je potrebné využiť príkaz switch, kde zahrnieme všetky vstupy, ktoré by mohli byť prijateľné + pridáme default možnosť, teda tú, ktorá sa vyvolá ak sa parameter v možnostiach nenachádza a z 99% pravdepodobnosti to mohol byť práve škodlivý vstup.

Zhrnutím vieme, že prvou a najdôležitejšou ochranou nášho systému je ochrana proti SQL Injection. Ak si náš systém proti tomuto neuchránime, doslova dobrovoľne dávame prístupy všetkým náhodnym útočníkom, ktorí budú samozrejme skúšať, či si niečo nezískajú ako účty, prístupy atď.

Ak vás zaujíma prečo nie je dobré používať addslashes(), ale funkciu mysqli_real_escape_string(), pozrite sa na nasledujúcu otázku, kde sa už toto niekto pýtal (v otázke je spomenutá funkcia mysql_real_escape_string(), je to to isté, ale mysql_* je už zastaralé a v budúcnosti sa celkom zruší).

Komentáre

Jakub píše:
11.4.2017 15:29:46[odpovedať]

Ahoj. Super praca. Chcel by som sa spytat ci mam osetrovat aj pripady, kde mi skript zacina napr. $id = $_GET['id']; ale toto $id nepouzijem dalej do query, ale na nejake ine ukony. Dakujem

Odoslať komentár

Posledné Referencie

Deffender BG & FUN private server Eduard Karpiel - portfólio VideoVizitky.SK Apartmány Maladinovo Lymfoštúdia Eva Czech Imperial Server

O Mne

Mám 21 rokov. Študujem Informatiku v odbore programovanie a taktiež sa venujem tvorbe nových webstránok. Pracujem s jazykmi

  • PHP (OOP) & MySQL
  • HTML & CSS (len drobné zmeny)
  • Symfony2 Framework (začiatočník, ale učím sa rýchlo - viď tento web)

Kontakt

V prípade záujmu alebo nejakého dotazu ma neváhajte kontaktovať na nižšie uvedených možnostiach. Poprípade môžete využiť kontaktný formulár

ICQ: 357-248-017
Skype: lkopo__ (upřednostňujte)
E-mail: