Aplikační data čistá jako lilie
V článku o Cross-site
scriptingu jsem se dotkl myšlenky, že není vhodné na začátku skriptu
prohánět vstupní parametry funkcí htmlspecialchars().
Doporučoval jsem místo toho aplikovat toto ošetření až bezprostředně
při odesílání dat na výstup. Rád bych nyní téma blíže rozvedl. Proč
je paušální použití zmíněné funkce na začátku aplikace špatné?
Už zase to MVC
Pro pochopení, v čem je chyba, si představte klasickou MVC architekturu. Na stejné systémové rozpory nakonec narazíte snad i při jakékoliv jiné architektuře, ale na MVC je to vidět snad nejlépe.
V architektuře MVC se aplikace sestává ze tří částí – Modelu, View a Controlleru. Zjednodušeně řečeno Controller zajišťuje interakci vůči požadavkům uživatelů a jejich primární zpracování. Model poskytuje business logiku a veškerou manipulaci s aplikačními daty. View pak výstup na zobrazovací zařízení uživatele.
Základní myšlenkou MVC je předpoklad, že když kteroukoliv z těchto částí vyměníte či nahradíte, nemělo by to nijak ovlivnit zbývající dvě části. Většina webových aplikací tak má View zajišťující HTML výstup do prohlížeče. Pokud jej ale nahradím jiným, které poskytuje třeba výstup do WML, odpověď přes SOAP nebo export do PDF, mělo by to být možné, aniž bych musel cokoliv měnit v Modelu nebo Controlleru.
Kontaminujeme aplikační data
Celý problém je už asi zjevný. Funkce htmlspecialchars() je
totiž úzce svázána jen a pouze s výstupem do HTML.
Patří tedy ze své podstaty do View generujícího webové stránky. Nikde
jinde v aplikaci nemá co dělat.
Když ji použijeme na vstupní data hned na začátku běhu aplikace (tedy
v Controlleru či Modelu), kontaminujeme si veškerá aplikační data
balastem, který je svázán jen s HTML formátem výstupu.
Například z názvu firmy Pepa & syn budeme mít
v databázi uložené Pepa & syn.
Budu-li chtít později vygenerovat třeba PDF soubor, mám zásadní
problém. Musím předtím nějak všechny &,
" a jim podobné textové entity pracně nesystémově
nahrazovat zpět za původní znaky.
Vše má své místo
Správné řešení je jen jedno – systémově aplikovat všechna ošetření právě na těch místech, kam patří. Pokud je cokoliv svázáno s konkrétním typem výstupu, je nutné to aplikovat právě až při generování daného výstupu.
Do Modelu a tím potažmo i do databáze patří pouze čistá aplikační data, nezatížená a nekontaminovaná jakýmkoliv konkrétním typem View. Jenom tak budou připravena pro výstup do jakéhokoliv dnešního i budoucího formátu.
Proklatě magické uvozovky
Ze podobného soudku je fungování direktivy
magic_quotes_gpc. Měla původně chránit aplikace před
SQL injection tím, že ve vstupních datech opatří všechny citlivé znaky
zpětným lomítkem, čímž z hlediska databáze zruší jejich speciální
význam. To má ale smysl dělat až bezprostředně při vkládání
vstupních dat do SQL dotazu:
mysql_query('SELECT *
FROM uzivatele
WHERE jmeno = "' . mysql_real_escape_string($_GET['jmeno']) . '"');
Tím, že mechanizmus magic_quotes_gpc ošetřuje paušálně
všechna vstupní data, a navíc ještě před začátkem provádění
samotného skriptu, zanáší do aplikace nesystémovost popisovanou v tomto
článku. V důsledku toho pak máme v aplikaci proměnné plné
zpětných lomítek, která tam vůbec nemají co
dělat.
echo $_GET['id'];
Pokud uvedený skript zavolám s parametrem ?id=joe's garage,
aplikace mi při zapnuté direktivě vypíše joe\'s garage.
Kdybych chtěl získat to, co uživatel opravdu zadal, tedy joe's
garage, musím celý parametr explicitně prohnat funkcí
stripslashes(). To je ale samozřejmě úplně postavené na hlavu
jakožto důsledek celé nesystémovosti takového řešení.
HTML do databáze nepatří
Dalším obdobným příkladem nevhodného postupu je ukládání vlastně jakéhokoliv HTML kódu do databáze. Ani ten tam logicky nepatří, protože je opět spojen s jedním typem výstupu. A při generování nějakého e-mailu nebo PDF bych musel opět řešit, jak se vypořádat s uloženými HTML tagy apod. Je ovšem pravda, že zrovna tady praktická stránka věci bohužel většinou vítězí nad snahou o systémovou čistotu a třeba při ukládání článků v CMS sypou HTML do databáze skoro všichni.
Radši ale upozorním na jednu věc. Pokud říkám, že HTML kód nepatří do databáze, myslím tím databázi v rámci Modelu, neboli v rámci základních aplikačních dat.
Jsou ale případy, kdy je ukládání HTML kódu nebo dat prohnaných skrz
htmlspecialchars() zcela v pořádku. Mám na mysli typicky
databázové cachování částí vygenerovaného kódu nebo
i celých výstupních stránek. Tady je ovšem nutné si uvědomit, že tato
část databáze nepatří do modelu, ale je jakoby součástí
View.
