Pokud provozujete dynamický web, v němž je veškerý obsah webu uložen v databázi, pak určitě časem narazíte na tuto důležitou "drobnost", která vás může trochu překvapit.
Dejme tomu, že máme připravenu administraci k článkům zobrazovaným na webu. V této administraci si pak načítáme dynamicky obsah článku do tagu <textarea>, abychom mohli články pohodlně editovat. Vygenerovaný zdrojový text části formuláře může vypadat třeba nějak takto:
<textarea cols="55" rows="15" name="Text"><p>Obsah článku</p></textarea>
Vše funguje v pořádku až do té doby, než se pokusíme jako HTML vložit nějaký speciální znak, třeba "<" či ">". Samozřejmě chceme, aby se tyto znaky pak správně zobrazovaly v HTML jako "zobáčky" a nebyly převedeny a interpretovány jako HTML tagy. Pokud trochu ztáte HTML, tak víte, že je musíme vložit speciálně jako < a >.
Člověk by si řekl, že když do formulář napíše tento text:
<p>Obsah článku</p>
<p>Ukázka zobáčku < a ></p>
a pak text z formuláře uloží do databáze, bude vše OK.
Ono opravdu všechno v pořádku je. Problémek nastane, když se pokusíme text opět vyeditovat a jednoduše si ho načteme z databáze přímo do tagu <textarea>. Ještě před načtením textu musíte totiž zvolat funkci HTMLSpecialChars($Text). Pokud to neuděláme, "zobáčky" < a > se přeuloží jako obyčejné znaky < a > a při zobrazování budou interpretovány jako HTML tagy.
Zde je jednoduchá ukázka kódu. V proměnné $Text bychom měli načtený text z databáze, ale já si ho tady ručně naplním:
<?
...
$Text = '<p>Obsah článku</p><p>Ukázka zobáčku < a ></p>';
echo '<textarea cols="50" rows="5" name="Text">'.HTMLSpecialChars($Text).'</textarea>';
...
?>
Pokud bychom nezavolali funkci HTMLSpecialChars(), tak se prohlížeč bude chovat na první pohled divně. Má sice v tagu <textarea> srávný text včetně < a > zobáčků, které jsou správně speciálně zapsány, ale při zobrazení textu ve formuláři na to "zapomene" a zobrazí obyčejné "zobáčky" < a >. Prohlížeč pak při odeslání formuláře vezme tuto zobrazovanou hodnotu. Vlastně ani jinou možnost nemá, protože vy můžete libovolně změnit text napsaný ve formuláři. Rozhodující je tedy forma, jakou jsou znaky zapsány ve formuláři při prohlížení. Zpočátku jsem si říkala, že by prohlížeč nemusel převádět tyto speciální znaky na jejich obrazovou hodnotu, když stejně neiterpretuje ostatní HTML značky. Třeba odstavec <p>Obsah článku</p> také nepřevádí na odstavec. Pak jsem si však umědomila, že kdyby byl celý text zapsán pomocí těchto speciálních značek, byl by text úplně nečitelný. Někdo si může nechávat texty prohánět funkcemi HTMLEntities() či HTMLSpecialChars(), a to by pak byla hrůza. Takže je vlastně velice dobře, že se prohlížeč chová tak, jak se chová!
My s tím musíme počítat a toto chování "přebít" tím, že na text z databáze zavoláme funkci HTMLSpecialChars(), čímž všechny nebezpečené znaky převedeme na speciální znaky. A speciální znaky tímto převedeme ještě na "speciálnější", takže jsou pak ve formuláři vidět jako obyčené speciální znaky. Ve formuláři se pak text zobrazí správně, odstavce jsou pořád vidět jako odstavce <p>Obsah článku</p> a speciální "zobáčky" máme napsány speciálně jako < a >. Tak, jak text vidíme ve formuláři, tak se nám uloží do databáze a máme vyhráno.
Když pak text ukládáme do databáze, již funkci HTMLSpecialChars() nevoláme!
Zcela opačně se však budeme chovat v případě, nějakému uživateli zpřístupníme administraci databáze a umožníme mu nějaký text takto editovat. Pokud se tento text bude vypisovat na webu, je třeba uživateli zamezit ve vložení speciálních znaků v surové podobě. Nechceme přece, aby nám do databáze vložil nějaké HTML tagy, které nám následně při jejich načtení a zobrazení na webu, celý náš web rozhodí. Pak je lepší ještě před uložením do databáze prohnat celý vložený text přes HTMLSpecialChars(), nebo případně úplně všechny tagy z textu vyhodit, a to pomocí funkce Strip_Tags(). V databázi již máme bezpečný text a při jeho opětovné editaci a jeho načtení do formuláře v <textarea> již převod pomocí funkce HTMLSpecialChars() neděláme. Docházelo by pak k dvojitému převodu pomocí funkce HTMLSpecialChars(), a to by bylo nežádoucí.
Takže malé shrnutí na závěr:
- Když víme co děláme a chceme vkládat do databáze přímo HTML text, který se nám bude zobrazovat jakou součást HTML později na webu, tak při načítání textu do formuláře tento text proženeme funkcí HTMLSpecialChars(). Při vkládání do databáze neděláme v tomto směru nic dalšího.
- Pokud se chráníme před uživatelovým vstupem a nechceme, aby nám z webu nadělal paseku, tak při vkládání do databáze použijeme funkci HTMLSpecialChars(), případně funkci Strip_Tags(). Při následné editaci textu načteného z databáze již v tomto případě nepoužíváme funkci HTMLSpecialChars() pro upravení vstupu ve formuláři v tagu <textarea>.
Při zobrazovní na webu, obsah textu z databáze normálně načteme a zobrazíme, ať už šlo o uživatelův, či náš text.
Poznámka:
Používejte funkci Strip_Tags() s rozmyslem. Stačí totiž jeden neúmylsný "zobáček" < a vše za ním až do dalšího tagu je ztraceno. Za to by vás nemusel mít uživatel rád. Už by vám pak nepomohlo, že uživatel chtěl třeba matematicky napsat něco v tom smyslu, že "on" si vás váží více než sebe a použil by formulku "já" < "Vy". Uložilo by se pouze "já", a "Vy" byste byl navždy ztracen. Byl jste za zobáčkem a odřízl jste se funkcí Strip_Tags() sám. Naopak, uživatel brzy přijde na to, že když napíše "já" > "Vy", tak mu to projde a řekne si, že tak je to vlastně správně :)
Online htmlspecialchars() function
- Online htmlspecialchars() function – tools4noobs.com