Zabezpečení webových aplikací je jednou z nejdůležitějších položek jejich vývoje. Článek analyzuje možnost napadení aplikace prostřednictvím klientských skriptovacích jazyků a řeší možná zabezpečení v prostředích PHP a ASP.NET.
Security of web applications I. - client script languages
Abstract
Security of web applications is one of the most important aspects of their development. This paper analyzes possibility of attack to application with use of client script languages and shows possible way of security in PHP and ASP.NET environments.
Úvod
Otázka zabezpečení webových aplikací je bezesporu nezanedbatelnou položkou, které musí
být při vývoji zejména programové a kontrolní části webu věnován dostatečný prostor.
Stále totiž existují principielně jednoduché metody, jak narušit aplikační bezpečnost, a
ve většině případů k tomu stačí přístup na úrovni běžného uživatele webu. Problém je však
často (zejména začínajícími vývojáři) ignorován, což útočníkům značně zjednodušuje
situaci. Tato část článku se zabývá zneužitím klientských skriptovacích jazyků a napodobováním
existujícího rozhraní pro získání přístupu k citlivým údajům. Popisuje také možnou obranu,
zejména v prostředí PHP, které je vůči těmto útokům daleko náchylnější.
Cíle útoků
Cílem útoků vedených za účelem získání citlivých dat je ve většině
případů session ID. Systém sessions (relací) je jednou z cest, jak kontrolovat pohyb
uživatele na webu (neboť protokol http je standardně bezstavový). Princip sessions je
jednoduchý [1] – každý přistoupivší uživatel obdrží jednoznačný identifikátor, tzv.
session ID (SID). Každá session může na serveru adresovat téměř neomezený prostor
vlastních proměnných, je tedy možné jednoduše ukládat na serveru stavové informace.
Jediným přístupovým údajem k těmto informacím je SID. Manipulace se SID je tedy
pochopitelně základem většiny útoků.
Session ID se totiž musí uložit nejen na serveru, pamatovat si ji musí
i prohlížeč (klient). Ukládání tohoto parametru se provádí buď pomocí systému cookies,
nebo (nejsou-li cookies k dispozici) pomocí URL – což je ale z bezpečnostního hlediska
velmi nešťastné řešení, neboť jde o profilovaný a vysoce používaný parametr, který se může
ukládat na více místech (historie prohlížeče, cache, statistiky, HTTP hlavička apod.).
Útočníkovi pak stačí dostat se k takové informaci - k tomu slouží také např. metody
spojené s přesvědčováním neznalého uživatele (klamavé reklamy apod.). Jednou z
procedur, která může bránit získání informací pomocí SID, je její životnost, která se
definuje podobně jako u cookies a po uplynutí dané doby ukončuje platnost session. Tím se
dá do jisté míry zabránit zpracování získaných informací vypršením určité lhůty (pokud tedy není
automatizované - potom doba zpracování není klíčový aspekt).
Velmi specifickým cílem je poškozování systému; v první řadě je třeba
rozlišit, zda byl útok cílený nebo vznikl nevědomostí (omylem) uživatele a chybou
aplikace. Cílené útoky většinou spočívají ve vkládání nebezpečných klientských skriptů,
které spustí nějakou operaci. Ta může být relativně nedestruktivního charakteru (úpravy
vzhledu, poškození za účelem chybné validace kódu), nebo vyloženě destruktivní (smazání
dat, nevratné poškození aplikace).
Typy útoků, XSS
Pro útoky, které jsou založeny na vložení nebezpečného skriptu do
obsahu webové stránky se používá termín Cross Site Scripting (XSS). Spočívají v
záměrném vložení nebezpečného kódu (který je napsán v klientském skriptovacím jazyku –
např. Javascript, JScript, VBScript) do „bezpečného“ obsahu webové stránky. Tyto techniky
často spoléhají i na sociální inženýrství (protože k úspěšně provedenému útoku je třeba
vykonat skript v prohlížeči napadeného uživatele, a někdy je proto nutné donutit jej ke
spolupráci). Rozeznáváme v základě tři typy těchto
útoků: okamžitý, perzistentní a lokální.
Okamžitý útok
Okamžitý útok (v originále non-persistent nebo reflected) je založen
na okamžitém zobrazení (vykonání) nebezpečného skriptu. V praxi jde o webové aplikace,
které zobrazí výsledek okamžitě na základě požadavku, tento však není dále uložen. Na
první pohled nejde o podstatný problém, neboť nebezpečný kód zpracuje ve většině případů
jen webový prohlížeč útočníka. Pokud však webová aplikace využívá parametry z URL (což s
největší pravděpodobností dělá), může útočník přesvědčit existujícího uživatele, aby
kliknul na odkaz s vloženou nebezpečnou skriptovací konstrukcí, která následně může
ukrást údaje, uložené v prohlížeči uživatele. Interní logika prohlížeče toto nebere jako
prohřešek, protože skript, který byl vykonán, náleží webové aplikaci a tato má právo k
přístupu k těmto informacím.
Obětí okamžitých útoků se nejčastěji stávají vyhledávací služby (což
vyplývá z jejich principu). V prosinci 2005 se objevuje bezpečnostní díra na systému
google.com, která je zdokumentována např. v [2]. Podstrčení škodlivého kódu spočívalo v
obcházení zabezpečovacího mechanismu systému použitím nestandardního kódování (konkrétně
utf-7) v URL.
Perzistentní útok
Daleko nebezpečnější je tzv. perzistentní útok (popisován termíny
persistent, stored). Ten využívá možnosti běžného uživatele vkládat vlastní obsah na web
(diskusní fóra, komentáře atp.). Útok potom může těžit z faktu, že špatně zabezpečená aplikace
vkládaná data nijak nekontroluje. Potom stačí vložit běžný HTML kód se skriptem, který
provádí nebezpečné operace. Ten bude opět posouzen prohlížečem jako bezpečný (neboť
náleží webové aplikaci). Rozsah poškození může být daleko větší než u předchozího typu útoku,
protože se může týkat všech uživatelů, kteří si danou informaci zobrazí ve svém
prohlížeči. Jde o velmi nebezpečný typ útoku, proti kterému by měla vždy existovat
alespoň základní ochrana. Typický příklad perzistentního útoku je popsán na obr.1.
Obr. 1 Typický příklad perzistentního útoku
Jeden z vůbec nejznámnějších útoků typu XSS byl proveden v říjnu 2001 na
webové službě hotmail, provozované společností Microsoft. Marc Slemko [4] popsal možnost
získání vitálně důležitých dat (session cookies a s nimi související přístup na službu
.NET Passport) pomocí jednoduchého emailu obsahujícího schovaný klientský skript. Tento
nebyl systémem odfiltrován a nic tedy nebránilo jeho spuštění. Velmi záhy se objevilo
mnoho dalších útoků na různé webově orientované emailové služby na celém Internetu, na
českém webu se tato situace dotýkala především služby email.cz. Jde o typický příklad
perzistentního útoku.
Lokální útok
Poslední typ útoku byl popsán relativně nedávno. Bývá v originále
často označován jako DOM-based nebo local. V podstatě je velmi podobný charakteristikám
okamžitého útoku, ale ke zpracování nebezpečného kódu se zneužije existujícího
klientského skriptu. Hlavní nebezpečí (ze kterého také plyne název útoku) spočívá v
lokálních webových aplikacích, protože nejrozšířenější z prohlížečů, Internet Explorer,
považuje javascripty spouštěné v lokální zóně za bezpečné a uděluje jim podle toho určitá
pravidla pískoviště (sandbox - označení prostoru proměnných, funkcí a možností,
která má v dané situaci klientský skript k dispozici). Lokální skripty mají např. možnost
přistupovat k souborům na lokálních discích. Z toho plynou evidentní nebezpečí (např.
odesílání soukromých dat třetí straně).
Tento typ útoku byl úspěšně použit např. v softwaru Bugzilla, což je
typický případ webové aplikace, běžící na lokálním prostředí. V této aplikaci byl
javascript použit k vypsání současné URL, pomocí DOM objektu document.location, bez
jakékoliv kontroly či filtrování. Toho bylo brzy zneužito ke vložení nebezpečného kódu
přímo do webové stránky, a zejména v Internet Exploreru tato chyba mohla mít dalekosáhlé
následky [3].
Zabezpečení proti XSS
Nejdůležitějším zabezpečením je nepropouštět do veřejných částí webu
vůbec žádné skriptovací konstrukce. Protože jazyk HTML je schopný zpracovávat kód
klientských skriptů i jako reakci na události jednotlivých značek (např. onchange, onclick)
nebo může měnit vzhled prvku z hlediska kaskádových stylů (parametr style), jedno z
nejlepších řešení je překódovat HTML ohraničovací značky <
a >
jako entity. Tímto
způsobem docílíme zobrazení zdrojového kódu přímo v textu, aniž by ho prohlížeč chápal
jako značky.
Řešení má pochopitelně svoje nevýhody. Někdy se můžeme dostat do
situace, kdy je pro nás žádoucí povolit uživatelům vkládat alespoň nějaké HTML značky
(např. obrázky). Pak je nutné toto ošetřit např. pomocí regulárních výrazů a úplně
odstranit některé parametry značek. Úplně nejlepším řešením je použít vlastní systém značek,
které převedeme v poslední etapě zobrazení na patřičné HTML tagy. Takovým systémem je
např. BBCodes (Bulletin Board Code), velmi známé zejména z diskuzních fór – implementuje
je např. systém phpBB [8] a mnohé další.
V PHP využijeme při ošetřování funkci htmlspecialchars()
,
která převede všechny HTML značky a speciální znaky na entity. Mírnou nevýhodou této
funkce je, že navýší objem výsledného přenášeného textu. Výhodou je téměř absolutní
bezpečnost – veškerý kód se zobrazí jako součást textu. Druhou možností je použít
ořezávací funkci strip_tags()
– ta odstraní veškeré HTML značky.
Nepřevádí však entity, což např. u znaku & (ampérsand), vyskytuje-li se volně v textu,
způsobí znevalidnění celé stránky – z čehož mohou plynout problémy (např. při použití
striktního formátu XHTML). htmlspecialchars()
doporučujeme používat všude tam,
kde očekáváme neformátovaný text (příp. text formátovaný jinak, než pomocí HTML).
Nejjednodušší způsob jak předejít tomuto útoku v ASP.NET je nastavit
vlastnost ValidateRequest v počáteční značce stránky na hodnotu true
:
<%@ Page ValidateRequest="true" %>
V tomto případě jsou všechny data z potenciálně nebezpečných vstupů
(formuláře, URL apod.) automaticky kontrolovány. V případě podezření, že vstupy mohou
obsahovat nebezpečná data jste přesměrováni na stránku upozorňující na tuto možnost.
Hodnota této vlastnosti je standardně nastavena hodnotu true
, tzn. pokud počáteční tag
tuto vlastnost neobsahuje je její hodnota true
.
Další možností ochrany ASP.NET stránek proti tomuto útoku je použití
funkce HtmlEncode(string s)
. Tato funkce se nachází v prostoru jmen
Server
a má stejnou funkci jako funkce htmlspecialchars()
v jazyce
PHP. Tuto funkce by se měla používat především v případech, kdy je vlastnost stránky
ValidateRequest
z nějakého důvodu nastavena na hodnotu false
[7].
Zneužití oprávněného požadavku
Další útok, typově velmi podobný XSS útokům, se týká zneužití
oprávněných požadavků registrovaných uživatelů na určitý systém. V originále bývá
označován jako cross-site request forgery (CSRF) [5], kde ze slova forgery
(padělání) je zřejmý princip tohoto útoku. Cílem útočníka je donutit registrovaného
uživatele, který je zalogován do určitého systému, k vykonání požadavku, který je v danou
chvíli z pohledu vztahu uživatel-systém oprávněný. Podobnost s XSS souvisí ve způsobu,
jak získat pro náš útok důvěru uživatele (což může být skutečně realizováno útokem na
podstatě XSS) a pomocí klientského skriptu je většinou realizován i samotný útok - nejčastěji
automatickým odesláním skrytého formuláře. Při samotném útoku se nikde nepřenáší důvěrné informace, neboť
vykonavatelem operace je v tuto chvíli pouze oprávněný uživatel. Protože velmi často
zneužíváme aktivní session, bývá tento útok také nazýván session riding.
Ochranou proti CSRF útokům je důkladné zabezpečení vstupních bodů u
důležitých operací. Typickým řešením je systém tokenizace, který je založen na
generování jednorázových přístupových hesel (tzv. token). Při vykonání požadavku se ověří,
zda odeslaný token je identický s očekávaným, operace se vykoná a token se zničí, aby
nemohl být použit znovu.
Méně spolehlivou cestou je kontrolovat, zda referer (součást HTTP
hlavičky, která udává adresu stránky, ze které uživatel HTTP požadavek vyslal) náleží
naší aplikaci. Protože některé firewally tuto vlastnost blokují a mažou, je to
nespolehlivá cesta, nehledě na fakt, že ne každý požadavek musí nutně pocházet z nějaké
předcházející stránky.
Poslední ochranou je dodatečná autentizace požadavkou, nejlépe mimo
webové prostředí. K tomu slouží (zejména u platebních systémů) externí nástroje jako
autorizační kalkulátor (generující náhodný kód na základě předem dohodnutých pravidel)
nebo autorizační SMS. Typický průběh útoku CSRF popisuje obr.2.
Obr. 2 Průběh útoku formou padělání požadavku
V jazyce PHP ani v ASP.NET neexistují konkrétní metody ochrany
proti tomuto útoku; ta spočívá ve využití výše zmíněných principů. Žádný citlivý
formulář by neměl být náchylný k padělání - zejména pokud pracujeme s platebními
systémy nebo důležitými osobními údaji.
Závěrem
Útoky využívající klientských skriptovacích jazyků představují dnes
jedno z největších rizik moderních webových aplikací. Jedině správnou ochranou vstupních
bodů naší aplikace můžeme dosáhnout požadované úrovně zabezpečení proti nim. Špatnou
cestou je spoléhat na bezpečnostní parametry webového prohlížeče (klienta); velmi častým
zdrojem problémů jsou totiž bezpečnostní díry v těchto aplikacích (z nichž na některé bylo
upozorněno i v článku).
Literatura
[1] Castagnetto J., Schumann S. a kol.: Professional PHP Programming, Wrox Press 2000.
[2] Amit, Y.: XSS vulnerabilities in Google.com, Watchfire Corporation 2005, online, dostupné z: http://www.cryptonomicon.net/msh/2005_12_01_mhamrick_archive.html
[3] Gervase, M.: XSS vulnerability in internal error messages, výpis z Bugzilla.org 1.12.2004, online, dostupné z: https://bugzilla.mozilla.org/show_bug.cgi?id=272620
[4] Slemko, M.: Microsoft Passport to Trouble, 5.11.2001, online, dostupné z: http://www.znep.com/~marcs/passport/
[5] Kolšek, M.: Session Fixation Vulnerability in Web-based Applications, ACROS d.o.o. 2002
Další zdroje
[6] Jakub Vrána - PHP triky (webová stránka), dostupné z: http://php.vrana.cz/
[7] How To: Prevent Cross-Site Scripting in ASP.NET (webová stránka), dostupné z: http://msdn2.microsoft.com/en-us/library/ms998274.aspx
[8] phpBB official page (webová stránka), dostupné z: http://www.phpbb.com/