Stripslahes, addslashes a magic_qotes_gpc - revisited

Není to tak dlouho, co jsem tu psal o obraně proti SQL injection v PHP a o užitečnosti funkcí zmíněných v nadpisu. Celkem důkladně jsem přitom argumentoval ve prospěch řešení, kdy jsou všechny vstupní proměnné skriptu protaženy přes funkci addslashes (buď ručně nebo automaticky) a to i za cenu toho, že se musí ve zbytku skriptu občas stripslashnout, na což se často zapomíná a může to vést ke zkomoleným výstupům.

Tento názor už ale nezastávám. Posunul jsem se totiž ve svých úvahách trochu dál – a jako obvykle mým "posunovačem" nebyla žádná velká teorie, ale tisíce řádek napsaného kódu.

Většina lidí si dříve nebo později totiž napíše na zacházení s databází knihovnu – ať už je to jenom nějaký jednoduchý wrapper zastiňující rozdíly mezi dvěma verzemi databázoveho enginu, nebo nějaká komplexnější potvora. A tak mě napadlo: Proč do této knihovny nepřidat i schopnost escapování parametrů? Když budete k databázi přistupovat pouze přes tuto knihovnu, nestane se vám, že zapomenete na addslashnutí. A po programu se vám nebudou potulovat řetězce se zpětnými lomítky, jako v případě postupu naznačeného v mém starém článku na toto téma.

Pro jednoduchou ukázku toho, co mám na mysli, použiju analogii klasické funkce sprintf – říkejme jí třeba sql_sprintf. Taková funkce bude mít stejně jako obyčejný sprintf jako první parametr řetězec, ve kterém budou zástupné symboly %s, %d apod., a za nimi bude seznam hodnot, kterými se mají nahradit. Rozdíl bude v tom, že u %s navíc předaná hodnota projde přes addslashes a vhodné bude i přidání apostrofů okolo ní (aby se dal předat řetězcový parametr, který je NULL).

Klasické volání databázového dotazu se změní z této podoby:

$result = mysql_query(
  "SELECT * FROM dogs WHERE name='" . $dogName . "' AND bitten_people < " . $n
);

na následující:

$result = mysql_query(sql_sprintf(
  "SELECT * FROM dogs WHERE name=%s AND bitten_people < %d", $dogName, $n
));

A je po problémech.

Přiznávám, že jsem zatím žádnou obdobu funkce sql_sprintf do svých knihoven nezačlenil, a tak tak zůstává čirým myšlenkovým konstruktem (můžete si ji zkusit implementovat za domácí úkol :-) Jediným důvodem pro to je, že všechny projekty, na kterých teď pracuji, už jsou v příliš pokročilém stadiu, abych v nich prováděl takové změny. Ale jakmile začnu tvořit něco nového, určitě se naznačený princip někde v mém kódu objeví. A troufnu si vám doporučit totéž.

Dec 6, 2004 – 13:12

Comments

Michal Altair Valášek
Ono MySQL neumí parametry? V .NET bych to proti MSSQL napsal nějak takhle:

Dim CMD As New SqlCommand("SELECT ... WHERE Neco=@neco")
CMD.Parameters.Add("@neco", Promenna)

SQL injection se nekoná, předává se to transparentně a navíc to přispívá k přehlednosti kódu...
David Majda
Pokud vím tak ne, MySQL jako API parametry neumí. Ale to, co tu naznačuješ, pokud to chápu správně, by se bez problémů dalo napsat jako knihovna. Ssem si i skoro jistý, že něco takového už existuje.
dgx
dgx
No konecne! :-))

Protažení všech vstupních proměnných přes AddSlashes je totiž prasárna. Nehledě na to, že každá databáze "slashuje" jinak, pro MySQL je třeba použít mysql_escape_string, pro ODBC zase zdvojování apostrofu a AddSlashes tohle všechno nemůže postihnout.

A protože správný programátor hledá obecné řešení, musí jeho skript umět pracovat i s dvěma různými databázemi zaráz (v praxi to bývá potřeba). Což je při násilném AddSlashes vstupních parametrů noční můrou.

Ještě k escapovací funkci je vhodné přidat i "quotovací", protože tady se databáze také liší.
DJ.Maca
DJ.Maca
Ja na to pouzivam tohle:

function TreatSQLQuery($in) {
global $db;

if (ini_get('magic_quotes_gpc')) {
$in = stripslashes($in);
}

return mysql_real_escape_string($in,$db);
}
David Majda
PEAR = fear :-)

Básník chce naznačit, že velmi nerad používá jakékoliv externí knihovny, a pokud to není problém, který by zabral víc jak odpoledne práce, vyřeší ho radši sám.

Důvody? Knihovny mají jiný "styl" než můj kód, často se blbě integrují do systému error reportingu (každá druhá knihovna si to řeší po svém), můžou zanášet chyby => nutno sledovat a připadně aktualizovat => spousta práce navíc.
johno
Básnik, ale týmto prístupom tak trochu potláča zmysel znovupoužiteľnosti kódu.

Nechcem nikoho do toho nútiť, ale poviem ti historku o jednom podobnom kóderovi. Áno, som to ja.

Kedysi som mal tiež štádium, keď som si všetko kódoval sám a odznova. Veľmi rýchlo som od toho upustil keď som zistil, že aktualizovať a opravovať knižnice, ktoré si spravím sám je oveľa zložitejšie ako pustiť pear upgrade-all. Takže ja mám skúsenosť presne opačnú. Kvalitné externé knižnice, ktorou bezpochyby PEAR::DB je práve čas šetria.

Na začiatku sa mi PEAR::DB zdal ako strašný moloch, ale čím dlhšie to používam tým viac sa mi to páči. Naozaj to stojí za to vyskúšať. Chce to si chvíľu zvykať, ale výsledky sa dostavia v podobe prehľadnejšieho kódu.

Čo sa týka error reportingu tak PEAR to má dosť striktné. Všetky knižnice v PEAR to musia používať rovnako.
David Majda
> výsledky sa dostavia v podobe prehľadnejšieho kódu

To je hloupost, přehlednost kódu se nezvýší integrací knihovny funkčně ekvivalentní s nějakou mojí interní.

> Všetky knižnice v PEAR to musia používať rovnako.

Sice rovnako, ale inak.

Problémy nastanou i ve chvíli, kdy si tu knihovnu musím nějak "přiohnout" - v tu ránu je na světě nová verze nekompatibilní s jakýmikoliv verzemi vydanými původním autorem. A to přiohnutí je dle mých zkušeností potřeba moc často.

Neříkám knihovnám jasné NE, ale že je potřeba zvážit všechna pro a proti. A třeba PEAR::DB je pro mě opravdu přítěž - a je mi jedno, kolik podporuje databází, protože mě stačí MySQL, a na ni si hezký wrapper napíšu za hodinu.
johno
> To je hloupost, přehlednost kódu se nezvýší integrací knihovny funkčně ekvivalentní s nějakou mojí interní.

Súhlasím.

Rob ako myslíš, ale uváž prosím nasledovné. Pekný wrapper si možno napíšeš za hodinu, ale stiahnutie a inštalácia PEAR::DB trvá niekoľko sekúnd. Samozrejme pokiaľ máš PEAR.

Navyše support a bug reporting ti robí niekto iný a tak sa o to nemusíš starať sám. Raz za čas to upgradnes a hotovo.

Čo sa týka úprav PEAR::DB pre vlastné potreby, tak to si neviem celkom predstaviť. PEAR::DB tu už nejaký rôčik je a spokojne to používajú tisíce ľudí tak nevidím dôvod prečo by tam nejaká závažná vec mala chýbať. Navyše keď ti tam niečo chýba tak to môžeš dať ako feature request na PEAR.

Podľa mňa máš len nejakú silnú averziu voči cudziemu kódu. Je to IMHO len prechodná fáza. Ja som bol na tom -- keď sa pozriem dozadu -- presne tak isto.
David Majda
S tím přiohýbáním máš konkrétně u PEAR::DB pravdu - tam to nebude potřeba skoro určitě.

Pokud bych začínal nějaký projekt úplně od nuly, možná by se o PEAR::DB dalo uvažovat. Ale vzhledem k tomu, že moje projekty začínají zkopírováním mojí soukromé knihonvy ICL (Impala Class Library, DB wrapper je její součásí) do adresáře projektu, čili nikoliv od nuly, tak se tím nezybývám. Teď už mi to nepoumůže a čas neušetří, ba naopak - musel bych cizí knihovnu pochopit, rozchodit atd. A pak bych třeba zjistil, že je mi to stejně nanic, protože to nesplňuje nějaký můj požadavek.

Máš možná pravdu, že mám obecně averzi k cizímu kódu. Většinou mi používání cizích komponent přineslo spíš komplikace a tak se snažím mít toho co nejvíc pod svojí kontrolou. Uvidíme, třeba mě to časem opustí :-)

Add comment

It is not possible to add comments to posts older than one month.