SQL injection
SQL injection je technika napadení databázové vrstvy programu vsunutím (odtud "injection") kódu přes neošetřený vstup, a vykonání vlastního, samozřejmě pozměněného SQL dotazu. Toto nechtěné chování vzniká při propojení aplikační vrstvy s databázovou vrstvou (téměř vždy se totiž jedná o dva různé programy) a zabraňuje se mu pomocí jednoduchého escapování potencionálně nebezpečných znaků.
sql injection a web
V klasickém případě je útok na internetové stránky prováděn přes neošetřený formulář, manipulací s URL nebo třeba i podstrčením zákeřně upravené Cookies. Bohužel, na internetu je stále velké množství webů, spravovaných převážně nezkušenými programátory, kteří o této technice útoku prostě neví a tuto kritickou chybu opomíjejí.
Ukázka útoku
Mějme program odesílající query do databáze:
statement := "SELECT * FROM uzivatele WHERE jmeno = '" + zadaneJmeno + "';"
Pokud však uživatel zadá jako jméno například:
a' or 'b'='b
aplikační program query doplní a odešle databázi ve formě
statement := "SELECT * FROM uzivatele WHERE jmeno = 'a' or 'b'='b';"
což může zapříčinit přemostění autorizační procedury, protože 'b' = 'b' je vždy pravda.
Pro SQL injection se samozřejmě dají použít všechny dostupné příkazy, takže pokud bychom v předešlém příkladě jako jméno zadali
a';DROP TABLE uzivatele; --
vypadala by query při odeslání servru jako
statement := "SELECT * FROM uzivatele WHERE jmeno = 'a';DROP TABLE uzivatele; --';"
čímž se nám povedlo smazat celou tabulku uživatelů. Poslední apostrof je pomocí sekvence -- zapoznámkován a nemá žádý vliv.
Samozřejmě podobných průniků je celá řada, díky klauzulím UNION a JOIN nejsme ani vázáni na tabulku předepsanou v části FROM a můžeme vypisovat data odkudkoliv z databáze.
Obrana
Na straně aplikace
Nejjednodušší obrana spočívá v vhodném escapování vstupu. Prakticky každý scriptovací program s podporou databáze má nějakou vestavěnou funkci pro escapování znaků před jejich použitím v query. Například pro Perl stačí využít
$query = $sql->prepare( "SELECT * FROM uzivatele WHERE jmeno = " . $sql->quote($zadaneJmeno) );
pro PHP (v příkladu implementace MySQL):
$sql = "SELECT * FROM uzivatele WHERE jmeno = " . mysql_real_escape_string($zadaneJmeno); mysql_query($sql);
Pokud si navíc ještě více potrpíme na bezpečnost (samozřejmě že se může stát že se na ošetření vstupu někde prostě zapomene), dá se navíc vypnout vypisování chyb. Pokud by pak útočník na nějaký napadnutelný vstup narazil, nedozví se o tom z chybových hlášení.
Na straně databáze
V databázi můžeme útoku zabránit (nebo ho přinejmenším extrémně ztížit) vhodným nastavením práv uživatele pod kterým bude program přistupovat. Málokdy je totiž třeba přímo z aplikační vrstvy mazat tabulky či dokonce databáze, proto stačí povolit základní SQL příkazy.