Dnes ráno jsem si všimnul dotazu ohledně parametrů v MySQL na zdejším fóru. Tazatel se ptal, jak v MySQL pracovat s parametry, načež mu bylo odpovězeno, že má SQL normálně poskládat.
Vřele doporučuji nikdy SQL dotazy neskládat, zaděláváte si tím na bezpečtnostní problém.
Skládat SQL dotaz je nebezpečné, protože pokud to neuděláte správně v celé aplikaci a byť jen na jednom místě zapomenete na patřičná ošetření, máte v aplikaci nezpečnostní díru.
Představte si následující situaci - ve webové aplikaci kontrolujete u přihlašovacího formuláře jména a hesla takto: "SELECT * FROM Users WHERE Uzivatel = '" + user + "' AND Heslo = '" + pass + "'". No a co udělá každý hacker? Zkusí jako uživatelské jméno dosadit tohle (bez uvozovek): "admin' –".
No a co se nestane? Apostrof ukončí řetězcový literál a za ním jsou dvě pomlčky, což znamená komentář - zbytek dotazu se tedy bude ignorovat. A uživatel má práva admina. Pokud to bude zlomyslný hacker, nedá za apostrof komentář, ale středník a pak tam přidá pár dalších příkazů, které mu z databáze vykopírují citlivá data uživatelů, nebo vám tam dá rovnou DELETE na všechny tabulky, co najde.
Jistě, dá se to ošetřit, málokdy ovšem stačí odstranit jen apostrofy (jsou i jiné způsoby, jak z literálu vyskočit). Každá knihovna pro práci s databází má funkci, která tohle nějak zařídí (v PHP např. známá mysql_real_escape_string). Jenže stačí, abyste v jednom dotazu zapomněli řetězec touto nebo podobnou funkcí prohnat a je konec. Právě proto se používají parametry - zaprvé pokud je striktně používáte všude (a na to jen tak nezapomenete), nemusíte se tohoto útoku vůbec bát, a zadruhé nemusíte se nemusíte starat o převádění datumu do formátu, v jakém ho chce databáze, to si většinou udělá už knihovna sama v okamžiku, kdy jí hodnotu parametru předáte. Naučte se tedy používat parametry.
Jak se to dělá v MS SQL?
Vytvoříme si klasicky připojení k databázi a klasický SqlCommand. V dotazu na místa, kam chceme něco dosazovat, umístíme parametry – před jejich názvem musí být @. My zde pro ukázku máme třeba @Datum. Pak pomocí volání com.Parameters.AddWithValue řekneme název parametru (už bez zavináče na začátku) a jeho hodnotu.
Using con As New SqlConnection("connection string")
Using com As New SqlCommand("SELECT * FROM Tabulka WHERE Datum > @Datum", con)
com.Parameters.AddWithValue("Datum", Now)
con.Open()
'klasické zpracování výsledku
con.Close()
End Using
End Using
A když už jsme u toho, nezapomínejte uzavírat spojení s databází. Spojení k databázi se berou z tzv. connection poolu, což je takový “bazének”, kde se spojení čvachtají, ráchají a hlavně čekají, až si je někdo vezme. Je jich ale omezený počet a pokud je nezavřete, do bazénku se nevrátí a nejsou k dispozici. Za chvíli tedy dojdou a aplikace začne padat.
A samozřejmě v tomto příkladu nejsou ošetřené výjimky, na druhou stranu pokud máte aplikaci správně napsanou, jediná možnost, kdy výjimka může nastat, je většinou pouze to, že databáze spadla nebo se k ní nedá připojit, a pak už je nejlepší napsat uživateli pouze výchozí chybovou stránku, protože stejně nemůžete nic rozumného udělat. Já tedy používám výjimky pouze tam, kde dělám nějaké složitější operace s daty (transakce atd.). U jednoduchých selectů je to celkem zbytečné.
Kdy parametry použít nejdou?
V opravdu mizivém procentu případů parametry použít nejdou, protože potřebujete SQL příkaz měnit nějak brutálněji, např. dynamicky určit název tabulky nebo sloupce, který se použije. Tam se parametr dát samozřejmě nedá. V takovém případě hlavně nenechte zadávat název tabulky přímo uživatele a buďte maximálně opatrní.
Tohle ale člověk většinou nepotřebuje tak často, já osobně stejně klasické selecty moc nepoužívám a většinu toho řeším pomocí uložených procedur v databázi.