SQL Injection

Co si pod tím představit?

SQL Injection je technika, kdy se hacker pokusí upravit/podstrčit vstupní hodnoty nějakého SQL dotazu tak, aby výsledkem dotazu byly tajná data, nebo aby poškodil databázi.

Jednoduchý příklad:

Máme na stránce registraci, přihlašování a editaci vlastních údajů (heslo). My se zaměříme na editaci údajů, protože tam může útočník něco vydolovat.

Zaregistruje se a otevře si editaci.

Útočník si domyslí, že nejspíše máme ve skriptu něco takovéhoto:

$query = "SELECT * FROM users WHERE id_user = '".$_GET['id']."' AND password = '".md5($_SESSION['password'])."'";
$res = mysql_query($query);
$row = mysql_fetch_array($res);
...

Tento zápis není zas tak hrozný (horší by byl bez té druhé podmínky), ale pokud je v nastavení PHP vypnuta direktiva magic_quotes_gpc, může útočník získat údaje všech uživatelů.

Jak to udělá? Velmi Jednoduše...

Napíše do URL místo svého id toto:

' OR ''='

celý dotaz pak tedy bude vypadat (otisk hesla je jen ilustrační):

SELECT * FROM users WHERE id_user = '' OR ''='' AND password = 'dfjd465456dfhkj'

Útočník ale opět vidí jen svoje údaje (poslední podmínka)..zamyslí se a zkusí toto:

 ' OR ''='' --

výsledný dotaz:

SELECT * FROM users WHERE id_user = '' OR ''='' -- AND password = 'dfjd465456dfhkj'

-- je totiž SQL komentář, a tak všechno za ním pozbyde významu.

Nyní již získá údaje prvního uživatele. Když dále pozmění vstup na:

 ' OR ''='' LIMIT 251,1 --

Získá údaje 251. uživatele...

Kdyby nebyla v php zakázana podpora více sql dotazů v jednom volání funkce mysql_query, mohl by útočník dokonce napsat:

 ' OR ''=''; DELETE FROM users  --

A smazat tak data celé tabulky...

Jak se tomu bránit?

Obrana je dvojího druhu:

  • Spolehnout se na správné nastavení PHP
  • Ošetřit to přímo ve svých skriptech

První metoda je dost jednoduchá: stačí zjistit, jestli je v PHP zapnuta direktiva magic_quotes_gpc - to zjistíme php funkcí phpinfo(). Pokud ano, máme vyhráno. Magic_quotes_gpc způsobí, že se před všechny ' vloží (slash). Tím přestanou ' plnit svoji funkci a přestanou být nebezpečné. Tuto metodu ale nedoporučuji, protože nikdy nevíte, kdy vám hosting tuto fuknci nevypne... A co pak? Než se vám podaří skripty přepsat, může vás někdo napadnout.

Druhá metoda je založna na ošetření všech vstupů, které se vkládají do SQL dotazů. Na to nám slouží dvě funkce: addslashes a intval.

Addslashes stejně jako magic_quotes_gpc předřadí uvozovky slashem (), čímž je zneškodní. Toto použijeme u všech vstupů, kromě čísel (stringy, datumy,apod.).

Na čísla pak použijeme funkci intval. Ta vrací vždy číslo. A to:

  • číslo, které jí bylo předáno
  • číslo, které se nachází na začátku řetězce, který jsme jí předali
  • 0, pokud jsme jí předali něco jiného než výše uvedené

Náš skript tedy bude vypadat:

$query = "SELECT * FROM users WHERE id_user = '".intval($_GET['id'])."' AND password = '".md5($_SESSION['password'])."'";
$res = mysql_query($query);
$row = mysql_fetch_array($res);
...

md5($_SESSION['password']) ošetřovat nemusíme...

Hodnocení

Komentáře

[1] Goues
2007-01-13 21:07:29

nepomohlo by jenom
$text = Str_replace("'","", $_GET['id']);
???

Na tento komentář odpověděl [2] Goues
Na tento komentář odpověděl [4] Dundee
[2] Goues
2007-01-13 21:07:59

#1 Goues: sakra, v těch uvozovkách se člověk docela ztrácí :D

[3] Radek
2007-01-13 21:46:42

Ja pouzivam pridavani a nebo kontrolu dat z databaze pres dvojite uvozovky. Je to taky nebezpecne ?

Na tento komentář odpověděl [5] Dundee
[4] Dundee
2007-01-14 15:38:38

#1 Goues: Nepomohlo. Vem to z druhé strany: Co když chceš dát do textu svého článku O'hara? Nebo třeba sem do komentáře (jako jsem to teď udělal :)) Kdybych měl ve skriptu str_replace, tak o tu ' přijdu. Pokud bych tam ale neměl nic, SQL dotaz na vložení komentáře skončí s chybou.

INSERT INTO `komentar` (..) VALUES ('Dundee','..','..','jjfjhjf O'hara sdfsdf')

tou uvozovkou vlastně ukončím řetězec a co je dál je úplně mimo...Buď se tedy vložení nepovede a nebo se to vloží jen po O (hara už tam nebude)...

Na tento komentář odpověděl [8] Goues
[5] Dundee
2007-01-14 15:40:01

#3 Radek: Jak to myslíš s těmi dvojitými uvozovkami? Napiš radši ukázku SQL dotazu.

[6] Dundee
2007-01-14 15:44:11

Ještě dodatek: když proženu vstup od uživatele funkcí addslashes, předřadí se všechny uvozovky zpětným lomítkem. Když ale potom chceme data z databáze vypsat, toto lomítko tam uvidíme. Např.: O\'hara. To samozřejmě nehceme, a proto musíme data z databáze, která chceme zobrazit, prohnat funkcí stripslashes(). Ta lomítka odstraní...

[7] Peane
2007-01-14 19:07:01

dik, docela mi to pomohlo vubec jsem nevedel co si pod tim amm predstavit a ted to vim tak dikes :-)

[8] Goues
2007-01-14 21:01:01

#4 Dundee: hmmm, pokud ovšem právě nechceš, aby je nepoužívali :D

Na tento komentář odpověděl [9] Dundee
[9] Dundee
2007-01-15 10:53:42

#8 Goues: Tak jasně, pokud chceš aby zadali číslo, proženeš to intval. Pokud ale zadávají nějaký řetězec, tak není úplně správné zakázat jim používat uvozovky. Někdo to opravdu ani nemusí myslet špatně....

Když na všechny vstupy od uživatelů(i na ty své - administrace), které zahrnuješ do SQL dotazů, použiješ intval a addslashes, tak máš prostě vystaráno...Můžeš psát cokoliv a skript to správně ošetří...

[10] Goues
2007-01-16 12:54:04

hmmm, občas mě štve že to někdo umí líp než já :D
ale jinak díky za všechny poučky :)))

2007-01-21 15:48:38
2007-04-13 12:36:28

function escape_string($_)
{
return get_magic_quotes_gpc() ? $_ : mysql_escape_string($_);
}

pouzit na retezce, ktere prisly zvenci, problem vyresen... Retezce vznikle uvnitr aplikace mam pod kontrolou a resim je podle toho co se do nich dalo...

Na tento komentář odpověděl [13] JAZBY
[13] JAZBY
2007-05-31 14:48:20

#12 Michal Tuláček: osobně si myslím že je stejně "jistější" používat adslashes.
Jinak autorovi díky. Pěkně napsané

2007-08-08 18:25:04

Velmi povedený článek. Rozhodně jsem se poučil.

2008-04-06 14:24:37

Uff, byť je to starší článek, tak na mě právě vykoukl z RSS čtečky a nemůžu si odpustit komentář, protože obsahuje dvě věcné chyby.

Každá databáze používá trošku jiný způsob "escapování" řetězců. Proto má také každá databáze k tomu určenou funkci. V MySQL je to mysql_real_escape_string(), v SQLite sqlite_escape_string(), PostgreSQL zase pg_escape_string().

Ten rozdíl může být skutečně dost podstatný: MySQL vkládá lomítka před apostrofy, MSSQL zase apostrofy zdvojuje.

Proto není vhodné používat "univerzální funkci", jako je addslashes(). Ale ještě mnohem problematičtější je direktiva magic_quotes_gpc, nechci se dlouhe rozepisovat, takže jen stručně: velkým oblouhem se jí vyhněte.

Druhou chybou je volání stripslashes při získávání dat z databáze. Nic takového nedělejte! Z databáze lezou data v čisté podobě. Je to logické - escapování se provádí, aby bylo vůbec možné syntakticky SQL příkaz vytvořit. U výstupu nic takové potřeba není.

Na tento komentář odpověděl [16] Dundee
Na tento komentář odpověděl [17] Dundee
2008-04-09 12:19:50

#15 David Grudl: Aha :) O funkci mysql_real_escape_string() jsem už někdy slyšel, ale nikdy jsem ji nepoužil. O tom, že z databáze leze čístý výstup jsem ale neslyšel nikdy :D

Doufám, že to chlapci z Programujte popíšou v novém seriálu o bezpečnosti lépe než já.

Schválně se pokusím zjistit, odkud vznikla tahle mystifikace, obávám se, že z nějakého článku na soom.cz.

Každopádně díky za opravu :)

2008-04-09 12:24:25

#15 David Grudl: Použití addslashes a intval jsem pochytil od Vrány.

http://php.vrana.cz/obrana-proti-sql-injection.php

Použití stripslashes asi vyplývá z použití addslashes, které oslashuje i jiné věci než jen ty co mysql_real_escape_string.

Komentáře již nelze přidávat