8. óra Weboldalak biztonsága Gyimesi Ákos gyimesi.akos@gmail.com http://webprog.gy-i-m.com
Bevezetés Kiket fenyeget veszély? a weblap üzemeltetőit: adminisztrátori jogosultságok szerzése adminisztrátori funkciók végrehajtása (pl. törlés) tetszőleges kód futtatása PHP-ban vagy adatbázisban a szerver üzemeltetőit: hozzáférés érzékeny fájlokhoz (pl. /etc/passwd) hozzáférés más honlapokhoz (shared hosting) a felhasználókat: jelszavuk, session-jük ellopása (XSS) nem kívánt műveletek végrehajttatása (CSRF)
Védekezés - alapok Alapszabály: MINDEN usertől jövő input gyanús!
Súlyos hiba 1: jogosultságkezelés Súlyos hiba 1: nincs jogosultságkezelés PHP oldalon <div class= product > <?php if($isadmin):?> <a href= delete.php?id=<?php echo $product->id;?> > töröl </a> <?php endif;?> </div> Ha csak ennyi a jogosultságkezelés, azt a hülye is ki tudja használni...
Súlyos hiba 1: jogosultságkezelés Ugyanez hidden mezőkkel: <form action= register.php > Username: <input name= username /> Password: <input name= password type= password /> <input type= hidden name= isadmin value= 0 /> </div> Hála a Firebug-nak, ezt egy hülye is át tudja írni...
Súlyos hiba 1: jogosultságkezelés Ugyanez egy jó keretrendszerrel: templates/register.php: <form action= register.php > Username: <input name= user[username] /> Password: <input name= user[password] type= password /> </div> register.php: $user = new User($_POST['user']); $user->insert(); new User() ész nélkül elfogad minden attribútumot => isadmin=1-et is, ha a böngésző azt átadja
Súlyos hiba 1: jogosultságkezelés Súlyos hiba 1. elkerülése: minden adatbázisművelet előtt ellenőrizzünk jogosultságot (a session-ből) adatbázisművelet paramétereit (pl. isadmin) szűrjük
Súlyos hiba 2: SQL injection Egyik leggyakoribb támadási mód pedig elkerülése egyszerű: paraméteres SQL login.php $conn->execute( SELECT * FROM users WHERE username='$_request[user]' AND pass='$_request[pass]' ); legfőbb ok: programozó lustasága (1 sorral rövidebb kód...)
Súlyos hiba 2: SQL injection Egyik leggyakoribb támadási mód pedig elkerülése egyszerű: paraméteres SQL exploit is egyszerű... login.php?username=admin'-- login.php?username=admin'; DROP TABLES-- login.php?username=admin' AND 1 OR 1=' http://xkcd.com/327
Súlyos hiba 2: SQL injection Hibás védekezési módok: addslashes() kijátszható: egyes karakterkódolásoknál egy betű második karaktere lehet \ vagy ' magic_quotes_sybase beállítás esetén \' helyett '' lesz...
Súlyos hiba 2: SQL injection Hibás védekezési módok: magic_quotes php.ini beállítás: minden GET és POST változóra lefut egy addslashes() ugyanúgy nem nyújt teljes védelmet ott is működik, ahol nem kéne (jelenség: input mezőkben egyszer csak megjelennek a \ karakterek...) megnehezíti az alkalmazás hordozhatóságát (vagy be van kapcsolva, vagy nincs...) PHP 6-ban (végre) eltávolítják
Súlyos hiba 2: SQL injection Rendes védekezési módok: mysql_real_escape_string() karakterkódolást figyelembe veszi DE: adatbázis függő kényelmetlenül hosszú a neve ;) csak akkor véd, ha a paramétert stringként használjuk! $conn->exec('select * FROM news WHERE id='. mysql_escape_string($_request['news_id']) ); exploit: http://example.com/news.php?news_id=1; DROP TABLE news
Súlyos hiba 2: SQL injection Rendes védekezési módok: paraméteres SQL-ek PHP5: PDO beépítve támogatja előtte: adatbázis absztrakciós réteg (pl. ADOdb) $stmt = $conn->prepare('select * FROM news WHERE id=?'); $result = $stmt->execute(array( $_REQUEST['news_id'] ));
Veszélyes műveletek Különösen veszélyes műveletek: include fopen() eval() preg_replace, eval mód echo preg_replace('/^([0-9]*)$/e', '\\1*\\1', "16"); // 256 Ha ezek paramétereiben user input van, NAGYON gondosan validáljunk!
Veszélyes műveletek Tipikus példa: include a honlap felépítése moduláris 1 bemeneti fájl (index.php), ez nyitja meg a modulokat index.php?module=news&id=2 <?php include modules/$_get[module].php ;?> ha nincs input validáció... index.php?module=../../../etc/passwd
Fájlfeltöltés Fájlfeltöltésnél az alábbiakra figyeljünk: a célkönyvtár legyen validálva nehogy lecserélhessen pl. egy PHP fájlt... a célkönyvtár ne legyen webről látható különben feltölthet egy PHP szkriptet... vagy: szűrjük a fájlok kiterjesztését, tartalmát a célfájl nevében ne lehessen /,.., \
register_globals register_globals a 2. leggyűlöltebb PHP funkció ez is php.ini beállítás (vagy van, vagy nincs) ha On, akkor minden GET-ben vagy POST-ban érkező paraméter globális változó lesz sok régi kód számít rá
register_globals register_globals Miért veszélyes? delete_product.php: $user = getcurrentuser(); if ($user->isadmin) { $isadmin = true; } if ($isadmin && isset($_get['id']) { deleteproduct($_get['id']); } exploit: delete_product.php?isadmin=1&id=123
További veszélyek Mindez szinte elegendő lenne, ha... a felhasználókat nem kellene megvédeni egymástól ha a szervert nem kellene megvédeni a programozóktól ha nem lennének spammerek...
Cross Site Scripting Felhasználók egymás ellen #1 Cross Site Scripting rövidítése: CSS, XSS lényege: kód injektálás az oldalra úgy, hogy azt más felhasználó megnézze ezáltal az ő böngészőjében lefusson a kód hatása: idegesítő működés (pl. JavaScript alert-ek) bizalmas adatok ellopása (ami az ő böngészőjében látszik, azt látja a JavaScript is) esetleg: tetszőleges fájl elérése a felhasználó gépén (böngészőhibák kihasználása)
Cross Site Scripting Tipikus példa: user fórum a beküldött üzenetek nincsenek (vagy rosszul vannak) escape-elve a támadó beküld egy JavaScript kódot, ami kiolvassa a felhasználó session cookie-ját konstruál egy láthatatlan űrlapot a HTML oldalon, mely a támadó szerverére mutat submit-olja az űrlapot
Cross Site Scripting Védekezés XSS ellen: escape-elés: output oldalon megjelenő input-ot szűrjük sima htmlspecialchars() nem elég: ugyanaz a karakterkódolásos probléma, mint addslashes()-nél megoldás: htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
Cross Site Scripting Védekezés XSS + session-lopás ellen: a session csak 1db IP címről működjön NAT-olt hálózaton nem nyújt védelmet mobil eszközök gyakran válthatnak hálózatot... session ID-t még véletlenül se tegyük URL-be! sok weboldal oda teszi, ha nincs cookie támogatás... PHP is alapértelmezetten így működik (kikapcsolható)
Cross Site Request Forgery Felhasználók egymás ellen #1 Cross Site Request Forgery lényege: a felhasználót rávenni arra, hogy egy linkre vagy űrlapra kattintson a link vagy űrlap a támadni kívánt oldalra visz, ahol egy műveletet hajt végre (pl. jelszóváltoztatás) mivel a felhasználó be van jelentkezve az oldalra, az ő nevében hajtódik végre a lekérés
Cross Site Request Forgery CSRF Példák: email-ben link: http://example.com/chpass.php?newpass=cracked ha ez feltűnne: http://tinyurl.com/dk2orr de még egy kattintás sem kell hozzá: <img src= http://tinyurl.com/dk2orr /> Sose engedjünk ilyen műveleteket $_GET-en keresztül!
Cross Site Request Forgery A $_POST-ra áttérés még nem teljes védelem: a linkelt oldalon ugyanúgy lehet egy űrlap, amit egy JavaScript automatikusan elküld Megoldás: ne lehessen ilyen oldalt kreálni!
Cross Site Request Forgery Védekezés CSRF ellen: HTTP referer vizsgálat: ez egy HTTP input header: honnan jön a kérés (hol volt a link, amire kattintottak) sajnos ezt ki lehet kapcsolni böngészőben + proxy-k is elronthatják + HTTPS-ről nem működik... Flash-ben kijátszható :( session élettartamának korlátozása nem ad teljes védelmet
Cross Site Request Forgery Védekezés CSRF ellen: összes módosító jellegű kérés authentikálása: session-höz egy titkos azonosítót rendelünk ezt az azonosítót minden űrlapba beletesszük hidden mezőként csak akkor fogadunk el egy űrlap kérést, ha szerepel benne ez az azonosító ezt a támadó csak akkor tudja, ha lehallgatta a forgalmat (ennyi erővel viszont a jelszavunkat is lehallgathatta volna...) fontos műveleteknél: captcha, SMS authentikáció stb.
Szerver-szintű védekezés Shared hosting problémák: sok webes felhasználó 1 szerveren ezek egymást is támadhatják... vagy az egész rendszert kiszolgálás általában: Apache+mod_php minden PHP szkript a www_data user nevében fut...
Szerver-szintű védekezés Egy elhibázott próbálkozás: PHP safe mode ez is egy php.ini beállítás PHP hívásokat letilt, vagy hatókörüket korlátozza: megadható a tiltott függvények listája (tipikusan: system, exec stb.) fopen(), readfile() stb: beállítható egy basedir, amin kívül nem nyúlhat csak olyan fájlok nyithatók meg, melyek tulajdonosa megegyezik a PHP szkript tulajdonosával megadható egy alapkönyvtár, amin kívül az fopen() stb. nem nyúlhat
Szerver-szintű védekezés Problémák a safe mode-al:.htaccess-el sokszor kikapcsolható néhány hívás korlátozása hiányos => ki lehet törni a safe mode-ból jogosultságproblémák fájlfeltöltésnél: egy fájl csak úgy nyitható meg, ha tulajdonosa megegyezik a PHP szkript tulajdonosával de a feltöltött fájlok a www_data user nevében jönnek létre... mail() korlátozások => hiányos paraméterezés esetén nincs hibajelzés, de nem megy ki levél külső programok hívása lehetetlenné válik
Szerver-szintű védekezés Valódi megoldás: suphp minden PHP szkript a szkript tulajdonosa nevében fut ehhez kell: mod_suphp + egy setuid-os (de apró) program elég új (első publikus verzió: 2006), ezért sok helyen még nincs bevezetve
Szerver-szintű védekezés Suhosin-patch (Hardened PHP): alacsony szinten: védelem a PHP Core-ban buffer overflow egyéb potenciális, esetleg nem ismert bug-ok magas szintű védelem: néhány veszélyes művelet (pl. eval) kikapcsolható néhány további ismert támadási mód elleni védekezés (pl. HTTP response splitting attack) session adatok titkosított tárolása állítható log-olás
Összefoglalás Védelemre szorul: a programozó a hacker-ektől az adminisztrátor a programozótól a felhasználók egymástól
Összefoglalás Nem árt, ha paranoiásak vagyunk, mert: a böngésző veszélyes üzem pl. JavaScript, Flash miatt a felhasználó könnyen félrevezethető ismerős nevében kapott email... sok betörés kevés próbálgatással, akár automatizáltan végrehajtható pl. Wordpress worm És akkor még nem is beszéltünk a böngészőhibákról!...