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éž.