spájame
slovenskú
IT komunitu
pridaj sa
Registrácia · Login

Ucitel 28.12.2012
Hodnoť článok:
6 6

Prepared statements, wtf?

Stale pouzivate v skriptoch mysql_ funkcie? Neviete co su to prepared statements? V tomto clanku sa naucime pristupovat k SQL databazam spravne a bezpecne.

Podstatou vacsiny PHP skriptov je ukladat a zobrazovat nejake data. Preto ma niekedy zaraza, ze taku jednoduchu a zakladnu vec, akou je bezpecna praca s databazou, vela koderov nezvlada. Su to aj ludia ktori programuju roky a dokola pouzivaju tie iste stare naucene postupy, alebo v tom horsom pripade deti, ktore sa PHP ucia z 10 rokov starych tutorialov na nete.

Escapovanie vs prepared statements

Existuju dva rozdielne pristupy k osetrovaniu pouzivatelskeho vstupu. Pri escapovani sa snazime pomocou PHP upravit pouzivatelov vstup tak, aby neobsahoval ziadne kontrolne znaky, ktore by mohli sposobit, ze sa nas SQL dotaz bude spravat inak ako chceme. Napr. znak " sa nahradi za \" (alebo pre niektore databazy sa zdvoji) a pod. Takto osetreny vstup potom dosadime do SQL dotazu a odosleme DB serveru. Jediny sposob ako bezpecne escapovat vstup pre MySQL databazu je pomocou funkcie mysqli_real_escape_string (mysql_real_escape_string)!

Prepared statements funguju inak, je to funkcia DB servera. Najprv odosleme DB serveru nekompletny SQL dotaz, kde pouzivatelske vstupy este nie su doplnene. Takto pripraveny dotaz volame "prepared statement". Nasledne DB serveru odosleme samotne pouzivatelske vstupy, ktore uz na strane PHP nemusime nijak upravovat. Tym mudrejsim citatelom uz asi doslo, ze databazovy server v tomto pripade vie ktore data su dotaz a ktore data su pouzivatelsky vstup, teda nemoze prijst k SQL injekcii.

Vyhody

Pri spravnom pouziti prepared statements nemoze vzniknut SQL injekcia. Bodka. Vsetky nase skripty su (co sa tyka komunikacie s databazou) bezpecne. Hura. Dalsou vyhodou je, ze jeden "prepared statement" mozme pouzit viac-krat s roznymi datami, co je rychlejsie ako viacnasobne spustanie samotneho SQL dotazu.

mysql_

Povodna implementacia pristupu k MySQL databaze pomocou mysql_ funkcii je zastarala, dlhodobo nevyvijana, v PHP 5.5 bude deprected (oficialne zastarala) a v niektorom z dalsich vydani bude uplne odstranena. Ak pouzivas mysql funkcie_ robis to zle. mysql_ funkcie nepodporuju prepared statements, bezpecne escapovanie je mozne pomocou funkcie mysql_real_escape_string.

Modernou alternativou k mysql_ funkciam tvoria mysqli_ funkcie alebo PDO. Obe moznosti podporuju prepared statements, bezpecne escapovanie a objektove API. mysqli_ funkcie su podobne ako mysql_, PDO poskytuje uplne nove, cito objektove API, pomocou ktoreho sa da pripojit na viac druhov databaz.

Priklad

V priklade si ukazeme ako pouzit prepared statements s PDO:

// pripojime sa k databaze
$pdo = new PDO("mysql:host={$adresa_db_serveru};dbname={$nazov_db}", $db_user, $db_heslo);

// vypneme emulaciu prepared statements - ta sa da pouzit pre databazy ktore nepodporuju prepared statements, PDO bude vstupy automaticky escapovat
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
 

// vytvorime prepared statement z nasho dotazu, vsimni si, ze namiesto pouzivatelskych dat je v dotaze otaznik
$statement = $pdo->prepare("SELECT * FROM pouzivatelia WHERE id = ?");

// dosadime do statementu konkretne pouzivatelske data
$statement->bindValue(1, $_GET['id_pouzivatela']);

// vykoname samotny dotaz s dosadenymi datami
$statement->execute();
 

// ziskame vysledok dotazu ako pole a vypiseme
print_r($statement->fetchAll());
 

// tiez mozeme jednotlive pouzivatelske vstupy pomenovat, co sa nam hodi pri dotazoch s viacerymi vstupmi
$statement2 = $pdo->prepare('INSERT INTO pouzivatelia VALUES (, :meno, :heslo, :email)');

// data ktore chceme do statementu dosadit mozeme predat aj ako pole priamo execute metode
$statement->execute(array(
    ':meno' => 'Ucitel', ':heslo' => '1337', ':email' => 'Ucitel@email.tld'
));

Pre viac prikladov ako pouzivat PDO citajt tu - http://php.net/manual/en/book.pdo.php - ak sa ti PDO nepaci a chcel by ste sa radsej vyskusat mysqli_ - http://php.net/manual/en/book.mysqli.php.

TL;DR

Nepouzivat mysql_. Pouzivat prepared statements pomocou mysqli_ alebo PDO. Pri spravnom pouziti nemoze nastat SQL injekcia. Bezpecne skripty s minimom namahy.

Toto mal byt povodne iba taky rychly tip ako pouzivat prepared statements v PHP, preto som sa nerozpisoval do detailov co je to SQL injekcia a pod., aj tak je to uz prilis dlhe. Neviem ci je takyto format ok, dajte vediet v komentaroch.

Obrazok - http://www.fanpop.com/clubs/my-little-pony/images/17940460/title/rainbow-dash-photo

Hodnoť článok:
6 6

28 komentárov k článku:

Komentovať môžu iba prihlásení

Zaregistruj sa cez bezplatnú registráciu alebo použi login cez Facebook (FB Connect)

Prihlás sa tu, ak už máš profil na Zajtra.sk:


Zabudol som heslo

0 0 julius 28.1.2015 07:35:44
@Ucitel Trosku oneskorene pisem, ale aspon vidis, ze clanok stale "zije. Mimochodom vdaka zan. Mam vlastny php framework a na zaklade tohto clanku som sa rozhodol, ze prislusne triedy prepisem do PDO-based.
1 0 Ucitel 31.1.2013 16:52:50
@Gabriel Potkány Hej, to je dost bezne WTF, ze neexistuje jednoduchy sposob ako copy-pastnut "hotove" query niekam inam na debugovanie.

Z toho co ponuka PDO vies zistit povodne query pouzite pre vytvorenie PDOStatement-u pomocou $stmt->queryString, rovnake query spolu so vsetkymi bindnutymi parametrami vies ziskat pomocou $stmt->debugDumpParams(), ktore ti ho vypise priamo na vystup (dost hlupa implementacia).

Ty vsak ale asi chces nejake "finalne query", ktore sa da priamo spustit, s "doplnenymi" parametrami. Pri pouziti skutocnych nativnych prepared statements vsak take query nikdy neexistuje, pretoze ako je vysvetlene v clanku, query a data sa posielaju aj samotnemu serveru osobitne.

Ani pri pouziti emulovanych prepared statements vsak PDO neposkytuje priamo ziadnu moznost ziskat finalne query. Mozes si ho poskladat sam pomocou str_replace/preg_replace a $pdo->quote() na escapovanie doplnovanych parametrov. Finalne query mozes ziskat aj z logu SQL servera, ak mas zapnute logovanie queries (neodporucam v produkcii).

Kedze som ti asi velmi nepomohol, tak na zaver ti ponuknem iny, lepsi, sposob ako debugovat queries v PDO a to pomocou metod $pdo->errorInfo() resp. $stmt->errorInfo(), ktore su PDO ekvivalentom funkcie mysql_error() a teda vratia kompletnu chybovu hlasku priamo od SQL serveru. Ak nastavis $pdo->setAttribute(PDO::ATTR_ERRORMODE, PDO::ERRMODE_WARNING), tak ti bude PDO hlasit chyby automaticky ako spravu na urovni E_WARNING, ak chces byt moderny, mozes pouzit hodnotu PDO::ERRMODE_EXCEPTION a bude ti namiesto chyb hadzat vynimky.

http://www.php.net/manual/en/pdostatement.debugdumpparams.php
http://www.php.net/manual/en/pdo.quote.php
http://www.php.net/manual/en/pdostatement.errorinfo.php
http://www.php.net/manual/en/pdo.error-handling.php
0 0 Gabriel Potkány 31.1.2013 01:21:23
Na takomto rieseni mi extremne vadi zlozitost debugovania sql dotazov. Pri starom sposobe si mozem dotaz jednoducho vypisat tym ze si vyechujem presne to co sa vopcha do mysql_query, ale pri bindovani v pdo mi to je na nic. Na stackoverflow pisali ze to nie je mozne pretoze sa dotaz skonstruuje az ked to dorazi do databazy, ale tu sa v clanku spomina ze pdo ma zapnuty emulacny mod takze by to asi ist malo. Moja otazka preto je ako sa spravne debuguju dotazy konstruovane cez PDO?
1 0 Ucitel 9.1.2013 22:07:10
@Nanobot Dakujem za komentar, pri pisani som sa ani nepozastavil pri tom ci pouzit alebo nepouzit diakritiku, kedze vacsinou programujem alebo pisem po anglicky tak pouzivam anglicke rozlozenie klavesnice a som zvyknuty pisat/citat texty bez diakritiky. Kazdopadne beriem na vedomie a dalsi clanok bude pekne s diakritikou.
Co sa tyka anglickych vyrazov tak mne osobne tiez pripadaju prirodzenejsie, ale urcite by sa zase nasiel niekto kto by kritizoval pouzivanie anglickych vyrazov na slovenskom portale. Preto sa snazim pouzit aspon tie pouzivanejsie slovenske vyrazy.
1 0 Nanobot 9.1.2013 19:43:22
Nechem byť hnidopich, ale videl som niektoré reakcie Ucitela a ak dobre vidím, tento článok je reakcia na iného hriešnika, čo tu publikoval článok ... ale nedá mi ... ak si preboha Učiteľ, tak láskavo používaj ako sebavedomý človek diakritiku. Čítať to, to je ako čítať po "analfabetovi", nerád tieto slová používam, ale keď sme šikovný IT národ, tak sa tak aj správajme. Ja som "učiteľ" IT a keby som hoci na FB videl žiakov bez používania diakritiky, nakladiem po prstoch. Samotný článok je OK, aj keď by som neposlovenčoval injection ako injekcia, predsa len v IT sa anglické výrazy bežne používajú a tiež to našich mladých učí si na také anglické výrazy zvykať, lebo aj keď sa angličtina učí, mnohí ju neovládajú, už vôbec nie technickú angličtinu a potom hľadajú v slovenčine niečo, o čom takmer nikto nepíše s použitím poslovenčeného výrazu a do angličtiny si to sami nevedia preložiť. Je potom žalostné, keď žiadate po niekom spustiť task manager a on ako "IT profesional" netuší, o čom je reč... :) Takže ... nabudúce prosím diakritiku, keď píšeš oficiálne články :)
0 0 Ucitel 3.1.2013 17:34:41
@Michal Macejko Porozmyslam nad tym ako by sa z toho dal vytvorit clanok z ktoreho by sa dalo aj nieco naucit, nie len copy'n'pastovat kod. Ale v principe by to vyzeralo podobne ako pise @Miroslav Petro, akurat vytvaranie nejakej pomocnej tabulky znie ako overkill.
0 0 Michal Macejko 3.1.2013 14:45:51
@Miroslav Petro super napad a vobec nie zlozity.. dakujem za tip :)
0 0 Miroslav Petro 3.1.2013 14:42:30
@Michal Macejko no robil som to trochu zlozitejsie, ale v principe slo o to, ze aktualne nastavenia sa ulozili do pomocnej tabulky a nove nastavenia prepisali tie povodne... po skontrolovani, ci vsetko je ok uzivatel dal zmeny ulozit - teda vymazala sa pomocna tabulka a priznak zmien sa odstranil... ak nebol spokojny so zmenami, z pomocnej sa znova vsetko nakopcilo do povodnej tabulky a priznak sa znova odstranil.. ak by opustil stranku bez nejakeho potvrdenia, tak priznaku vyprsala doba platnosti (k priznaku bol priradeny aj cas, ktory sa aktualizoval kazdou zmenou, ktoru vykonal uzivatel) a pokracovalo sa dalej ako keby zmenu neodsuhlasil(teda po kontrole priznaku uz inym uzivatelom sa zisti, ze vyprsal, tak sa zmeny vratia nazad a uzivatel vidi neupraveny web)... myslim, ze nejak takto to bolo....
0 0 Michal Macejko 3.1.2013 14:28:15
@Miroslav Petro nieco podobne, bolo by super ak by si to nasiel a mohol by som sa nato pozriet.. inac ako si kontroloval ze niekto opustil stranku a tym zmenit ten priznak v DB?
0 0 Miroslav Petro 3.1.2013 14:16:25
@Michal Macejko nieco podobne som davnejsie riesil... pri samotnej uprave sa zapise do DB priznak, ze sa ma ukazat vyrozumenie...pri generovani webu sa ten priznak kontroluje ako prvy... nieco podobne? mozno to este niekde najdem...
0 0 Michal Macejko 3.1.2013 14:08:03
@Ucitel.. ak mas chut stale napisat nejaky clanok.. tak ma nieco napada, co budem v blizkej budunocsti riesit, takze by mi to velmi pomoholo.. ako zabezpecit aby jednu webstranku mohol prezerat, respektive upravovat naraz len jeden uzivatel? Ostatny uzivatelia ktory budu chciet ist na stranku, na ktorej sa pracuje dostanu o tom vyrozumenie.. A to cele spojit nejak so zapisom do DB, napr. kto pracoval na stranke.. Neslo by o anomymnych uzivatelov, ale vsetko by boli registrovany.. Len tak ma v rychlosti napadlo ked som videl ze hladas inspiraciu :)
0 0 risontek22 1.1.2013 20:03:10
@viťo :D :D :D
0 1 viťo 31.12.2012 18:47:57
uroven clanku.. v nazve wtf.. sila
0 0 Ucitel 31.12.2012 13:17:57
Rozhodol som sa, ze skusim napisat este jeden clanok, mate nejaku temu, ktora tu chyba a radi by ste videli o nej napisany clanok (webdev, PHP/JS/C, unixy, hocico)?
1 0 Milan Bagin 31.12.2012 09:16:39
Clanok necitam od konca... preco komentare musim?
0 0 Roland Dobos 31.12.2012 01:18:14
@Miro Miťko Rosival sčasti ti musím dať za pravdu, ale @Ucitel vlastne len poukázal na to, že mysql_ funkcie budú deprecated, keďže veľa ľudí o tom nevie...
1 0 Roland Dobos 30.12.2012 12:40:58
good článok, akurát mi chýba diakritika... ale je to vpoho ;]
0 0 Ucitel 29.12.2012 15:58:52
@ritopich Zapnuta emulacia = escapovanie, teda realne to ani prepared statements nie su, nikdy sa nevola PREPARE a EXECUTE.
Vyhodne je to v tom, ze mozes programovat akoby si robil prepared statements a bude to fungovat aj s databazou, ktora ich nepodporuje, neskor ked budes chciet pouzit databazu s prepared statements staci ti pridat jeden riadok kodu. A ano, vo vacsine pripadov to bude rychlejsie.
Vyhodou prepared statements je ako som spominal v clanku nulova moznost SQL injekcie a vyssia rychlost v pripade, ze sa jedno query bude volat viac-krat s roznymi vstupmi.
Podstatne je, ze ak uz pouzivas prepared statements ci uz skutocne alebo emulovane, alebo mysqli_real_escape_string, tak si vyhral a tvoj kod je z hladiska SQL injekcie bezpecny.

@Miloš Nie je na to (AFAIK) ziadne elegantne riesenie, musis vygenerovat tu cast statementu a potom v cykle dosadit hodnoty, teda napr.:

$data = array(1, 2, 3, 4, 5, 6);
$stmt_in_section = substr(str_repeat(',?', count($a)), -1); // vrati "?,?,?,?,?,?"
$stmt = $pdo->prepare("SELECT * FROM users WHERE id IN ($stmt_in_section)");
foreach($data as $key => $val) $stmt->bindValue($key +1, $val);

Z tych dvoch riadkov si potom mozes vytvorit pomocnu funkciu alebo metodu. V realnych projektoch ale prevdepodobne nebudes pracovat priamo s PDO, ale s nejakou vyssou vrstvou postavenou nad PDO, ktora bude taketo veci ulahcovat.
1 0 Miloš 29.12.2012 12:08:17
Ako pomocou PS riešiť toto?

where id in (1, 2, 3, 4, 5, 6)

Na toto myslím, neexistuje nič vyhovujúce.
0 0 random 29.12.2012 01:38:57
hm a vcom je vyhodnejsie vypnutie emulacie ? je to rychlejsie ? alebo to ma nejaky iny dovod? lebo logicky dvojnasobna komunikacia by mala byt neefektivnejsia, ci
Zajtra.sk > Programovanie > PHP > Prepared statements, wtf?


Kritika

Vieš ako robiť veci lepšie? Pomôž našim odvážnejším členom a skritizuj im projekty!

Reklama

Seriály zo Zajtra.sk

Reklama