Co je Cross-site scripting jak mu předcházet

XSS neboli Cross-Site Scripting je jedna z nejstarších zranitelností webových aplikací. A protože jí stále mnoho webů, resp. webových aplikací trpí a většina uživatelů má JavaScript zapnutý, ukážeme si jednoduché příklady, jak zranitelnost vzniká a jak se jí bránit. Článek je věnován zejména těm, kdo o XSS zatím pořádně neslyšeli.

V tomto článku budeme vyžadovat alespoň základní znalosti HTML, CSS a JavaScriptu. Pro serverově orientované příklady byl zvolen značkovací jazyk PHP pro jeho jednoduchost a rozšířenost.

Na úvod: co mohou způsobit znaky „<“ a „>“

Začneme příkladem. Mějme jednoduchou stránku s jedním vstupním polem, které je součástí formuláře. Formulář odesílá svůj obsah na stejnou stránku a zobrazí obsah odeslaného políčka.

<html>
<body>
  <form>
    <input type="text" name="test">
    <input type="submit" value="Odeslat a zobrazit">
  <br>
<?php
if (isset($_GET['test'])) {
    echo 'Odeslaná hodnota je: ' . $_GET['test'];
}
?>
  </form>
</body>
</html> 

Co se stane, když do našeho formuláře zadáte slovo „pokus“? Na výsledné stránce se zobrazí slovo „pokus“. Co se ovšem stane, když k němu přidáte značky HTML a zadáte např. „<b>pokus</b>“? Zobrazí se tučně slovo „pokus“.

Jinými slovy – v tomto případě můžete libovolně měnit HTML stránku. Uvedený příklad je jedním z mnoha případů XSS. Kromě celkem neškodné značky pro tučné písmo můžete vložit i kód spouštějící JavaScript (odtud také pochází název Cross-Site Scripting). Podívejte se na několik ukázek možných vstupů a na jejich výsledky (možností je ale daleko víc):

uživatelský vstup akce v prohlížeči
<b>pokus</b> zobrazí se „pokus“ (tučně)
<img src=x onerror=„aler­t(‚XSS‘);“> vyskočí javascriptové okno s nápisem XSS
<img src=„http://h­acker.example­.cz“> zobrazí se obrázek z útočníkovy stránky

Jak se útoku bránit? V našem případě postačí, když nahradíme znaky „<“ a „>“ HTML entitami, tedy „&lt;“ a „&gt;“.

Pro začátek předvedeme tu nejjednodušší funkci, která je k dispozici snad v každém programovacím jazyce – nahrazování výskytu řetězce za jiný – v PHP je to str_replace. Uvedený kód tedy změníme na následující:

echo 'Odeslaná hodnota je: ' . str_replace(array('<', '>'), array('&lt;', '&gt;'), $_GET['test']); 

Tím jsem uvedený příklad ochránil proti XSS. Ale pozor, tato obrana není v některých případech dostačující. Proto čtěte dál.

Co může způsobit uvozovka nebo apostrof

Rozšíříme předchozí příklad – pro snadnou změnu během testování zobrazíme odeslanou hodnotu i v textovém poli.

<html>
<body>
  <form>
    <input type="text" name="test" value="<?php echo $_GET['test']; ?>">
    <input type="submit" value="Odeslat a zobrazit">
    <br>
<?php
if (isset($_GET['test'])) {
        echo 'Odeslaná hodnota je: ' . $_GET['test'];
}
?>
  </form>
</body>
</html> 

Po vyzkoušení uživatelských vstupů z první tabulky se může zdát, že je vše v pořádku. Ale to je omyl, viz následující vstupy:

uživatelský vstup akce v prohlížeči
„><img src=“http://h­acker.example­.cz/obrazek.jpg“><x x=“ zobrazí se obrázek z útočníkova serveru
“ onclick=„aler­t(‚XSS‘);“ x=“ vyskočí javascriptové okno po kliknutí do políčka

Kromě znaků „<“ a „>“ musíme totiž ošetřit i uvozovky („) a apostrofy (‚), podle toho, co používáme. V PHP se pro tento účel používá funkce htmlspecialchar­s().

<input type="text" name="test" value="<?php echo htmlspecialchars($_GET['test'], ENT_QUOTES); ?>"> 

Co v případě značky textarea?

V dalším příkladu předvedeme ještě jedno ošetření HTML, kde nemusí být na první pohled jasné, proč jej vůbec dělat. Na první pohled se totiž může zdát, že není nutné nic ošetřovat. Mějme následující příklad:

<html>
<body>
  <form>
    <textarea name="test"><?php echo $_GET['test']; ?></textarea>
    <input type="submit" value="Odeslat a zobrazit">
  </form>
</body>
</html> 

Pokud budete zkoušet prozatím uvedené uživatelské vstupy, značku textarea neprolomíte. Zkuste ale dodat do uživatelského vstupu její vlastní uzavírací značku.

uživatelský vstup akce v prohlížeči
</textarea><img src=„http://h­acker.example­.cz/obrazek.jpg“> zobrazí se obrázek z útočníkova serveru

A obrana? Jako v prvním příkladu – převádět znaky „<“ a „>“ na jejich HTML entity, například pomocí funkce htmlspecialchar­s().

Obrana v samotném JavaScriptu

Výše uvedené příklady měly jedno společné – uživatelský vstup se ošetřoval na straně serveru. A co když nás serverová strana zrovna nezajímá, můžeme si nezabezpečená data ošetřit i na klientské straně? Ano, i to jde, ukážeme si další příklad:

<html>
<body>
  <script>
    function zobraz() {
      document.getElementById('vysledek').innerHTML =
           'Odeslaná hodnota je: ' +
           document.getElementById('test').value;
      }
  </script>

  <input type="text" id="test">
  <input type="submit" onclick="zobraz();" value="Zobrazit">
  <br>
  <div id="vysledek"></div>
</body>
</html> 

V příkladu je použita metoda innerHTML(), ta umožňuje upravit (resp. nahradit) část HTML dokumentu. Vyzkoušejme následující vstupy:

uživatelský vstup akce v prohlížeči
pokus zobrazí se „pokus“
<b>pokus</b> zobrazí se „pokus“ (tučně)
<img src=„http://h­acker.example­.cz“> zobrazí se obrázek z útočníkova serveru
<b onmouseover=„a­lert(‚XSS‘);“>n­ajeď myší přes tento text</b> vyskočí javascriptové okno s nápisem XSS

Obrana je stejná jako u prvního příkladu: nahradíme nebezpečné znaky jejich HTML entitami. V JavaScriptu použijeme metodu replace().

function zobraz() {
document.getElementById('vysledek').innerHTML =
       'Odeslaná hodnota je: ' +
       document.getElementById('test').value.replace(/</g, '&lt;').replace(/>/g, '&gt;');
} 

Míchanice: HTML + PHP + JavaScript

PHP svou jednoduchostí přímo svádí k míchání kódu (např. v samotném HTML použijeme pro výpis hodnot PHP), a tak snad nepřekvapí ani generování JavaScriptu z PHP. Může se někdy samozřejmě hodit, ale opět je nutné si dát pozor.

<html>
<body>
<script>
  window.onload = function() {
    var value = 'Odeslaná hodnota je: <?php echo $_GET['test']; ?>';
    value = value.replace(/</g, '&lt;').replace(/>/g, '&gt;');
    document.getElementById('vysledek').innerHTML = value;
  }
</script>

  <form>
    <input type="text" name="test">
    <input type="submit" value="Odeslat">
    <div id="vysledek"></div>
  </form>
</body>
</html> 

Zobrazení hodnoty je zde již ošetřeno. Ale podle předchozích zkušeností s uvozovkami a apostrofy nás hned napadne, jak apostrof prolomit.

uživatelský vstup akce v prohlížeči
‚; alert(„XSS“); value = ‚není tu nic vyskočí javascriptové okno s nápisem XSS

Pokusíme se tedy ošetřit apostrofy a uvozovky, ale pozor, to není celá obrana. Existuje totiž další možnost, jak JavaScript ukončit. Je to značka pro ukončení skriptu: „</script>“ nebo i její kratší varianta „</script “ (mezera či jakýkoliv jiný prázdný znak na konci). Jakmile interpret JavaScriptu narazí na tento výskyt znaků, ukončí se javascript a dále se pokračuje ve zpracování HTML.

uživatelský vstup akce v prohlížeči
</script><scrip­t>alert(„XSS“)</sc­ript> vyskočí javascriptové okno s nápisem XSS
</script><img src=„http://h­acker.example­.cz“> zobrazí se obrázek z útočníkovy stránky

A jak se bránit v tomto případě? Nejjednodušší je opět nahradit „nebezpečné znaky“: < a > za jejich HTML entity.

Další případ: uživatelským vstupem je HTML

Výše uvedené příklady pracovaly jen s prostým textem a cílem bylo jej zobrazit. Existují situace, kdy uživatelským vstupem skutečně je HTML, typickým případem může být webové rozhraní e-mailu. Zde vyvstává řada dalších otázek:

  • Co s nevalidním HTML?
  • Budou se zobrazovat externí obrázky?
  • Povolíme kaskádové styly?
  • Co s JavaScriptem?
  • Co s formuláři?
  • … (a řada dalších)

Na jednu stranu chceme zajistit bezpečnost a na druhou stranu uživatel chce mít „hezky vypadající e-mail“ – prostě HTML. Existuje více způsobů, jak řešit tento problém, ale nejúčinnějším řešením je použití tzv. whitelistu – seznamu HTML značek a jejich atributů, jejichž vložení povolíme (protože jsou bezpečné). Příklad takového velmi krátkého whitelistu:

povolená značka povolený atribut
p
a href
b

Odpovíme na předchozí otázky s pomocí této tabulky:

  • Co s nevalidním HTML?
    Nevalidní HTML budeme muset nejdříve převést na validní HTML (pokud je tedy nechceme rovnou zahodit). Prohlížeče to také řeší (a bohužel každý trochu jinak), ideálně k tomu použijeme nějakou hotovou knihovnu.
  • Budou se zobrazovat externí obrázky?
    Ne, značka „img“ není v našem whitelistu povolena.
  • Povolíme kaskádové styly?
    Ne, značka „link“ pro načtení externího stylopisu není povolena, ani atribut „style“ není povolen.
  • Co s JavaScriptem?
    Odfiltruje se. Značka „script“ není povolena ani žádné atributy využitelné jako ovladače událostí nejsou povoleny (příkladem může být „onclick“) ani kaskádové styly nejsou povoleny (i ty by mohly vést ke spuštění JavaScriptu, viz níže). Nesmíme ovšem zapomenout zkontrolovat i obsah povoleného atributu href (ten by mohl obsahovat javascriptový kód uvozený prefixem „javascript:“)
  • Co s formuláři?
    Odfiltrují se. Značky „form“, „input“ ani další formulářové značky nejsou povoleny.

Z košatého HTML na vstupu se zahodí veškeré nepovolené HTML značky a atributy. Zůstanou jen ty povolené.

XSS a kaskádové styly

Kaskádové styly úzce souvisí s předchozí problematikou zobrazování uživatelského HTML. I zde existují možnosti, jak útočit a např. spustit JavaScript. Příkladem může být vlastnost expression dostupná v Internet Exploreru:

<div style="width: expression(alert('XSS'));"> 

Pro Firefox (a všechny prohlížeče postavené na jádru Gecko) existuje v kaskádových stylech také vlastnost, která umožňuje spuštění JavaScriptu (podrobnosti najdete v XSS Cheat Sheet), viz příklad:

<p style=-moz-binding:url(xssByCssInFirefox.xml#xss);>text</p> 

Řešení? Podobně jako v předchozím příkladu: whitelist, čili povolit jen ty vlastnosti, které opravdu povolit chceme.

Napsat vlastní HTML/CSS filtr není nic jednoduchého, proto pravděpodobně použijete nějakou hotovou knihovnu, v případě PHP např. projekt HTML Purifier.

Závěr

Tvůrci webů a webových aplikací by měli myslet na bezpečnost a nezapomínat pečlivě ošetřovat veškeré vstupy od uživatelů. Veškeré možné útoky včetně jejich podpory v nejpoužívanějších prohlížečích se dají nastudovat na XSS Cheat Sheet od RSnake.

Uživatelé Firefoxu se pak mohou chránit proti mnoha typům XSS pomocí rozšíření NoScript. Má však zpočátku otravnou, ale jinak velmi účinnou, vlastnost. Musíte NoScript nejdříve naučit, jaké weby mohou používat JavaScript ve vašem prohlížeči.

Odkazy


Autorem článku je Jan Pejša, vývojář webových aplikací ve společnosti Kerio Technologies s.r.o., která je jedním z hlavních výrobců bezpečnostního internetového softwaru pro malé a středně rozsáhlé sítě, se specializací na síťové firewally a bezpečnost interní firemní komunikace.

Absolvent ZČU v Plzni. V současné době pracuje jako vývojář webových aplikací ve společnosti Kerio Technologies s.r.o..

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 54

Přehled komentářů

danaketh RE: Co je Cross-site scripting jak mu předcházet
Keson RE: Co je Cross-site scripting jak mu předcházet
benzin RE: Co je Cross-site scripting jak mu předcházet
dan str_replace
xmorave2 Re: str_replace
dan Re: str_replace
Hoween Re: str_replace
Miroslav Juhos Re: str_replace
SFS Re: str_replace
Hoween Re: str_replace
SFS Re: str_replace
dan Re: str_replace
SFS Re: str_replace
dan Re: str_replace
n0 Re: str_replace
blek džek Re: str_replace
Anonym Re: str_replace
Anonym Re: str_replace
Anonym Re: str_replace
czexit Re: str_replace
Kuchař Re: str_replace
yossarian Re: str_replace
Jan Pejša Re: str_replace
Hoween Re: str_replace
noaco Re: str_replace
Hoween Re: str_replace
Martin Re: str_replace
Hoween Re: str_replace
jar Re: str_replace
Hoween Re: str_replace
Ped Re: str_replace
Hoween Re: str_replace
David Grudl Re: str_replace
Ja. prečítajte si tiež
Martin Hassman Re: prečítajte si tiež
David Grudl Chyba v escapování pro JavaScript
Jan Pejša Re: Chyba v escapování pro JavaScript
David Grudl Re: Chyba v escapování pro JavaScript
David Grudl Re: Chyba v escapování pro JavaScript
Jan Pejša Re: Chyba v escapování pro JavaScript
Jakub Vrána Re: Chyba v escapování pro JavaScript
Jan Pejša Re: Chyba v escapování pro JavaScript
David Grudl Ošetření znaku &
Jan Pejša Re: Ošetření znaku &
Hoween Re: Ošetření znaku &
David Grudl Re: Ošetření znaku &
Jan Pejša Re: Ošetření znaku &
Jakub Vrána Re: Ošetření znaku &
Standa RE: Co je Cross-site scripting jak mu předcházet
Jan Pejša RE: Co je Cross-site scripting jak mu předcházet
Hoween RE: Co je Cross-site scripting jak mu předcházet
karf RE: Co je Cross-site scripting jak mu předcházet
David Grudl Ještě jednou a tentokrát pořádně
Ash Jak bastlit pseudoochranu uživatelských vstupů.
Zdroj: https://www.zdrojak.cz/?p=2922