Mostanában, a dinamikus tartalmat szolgáltató web alkalmazások sarokkövének számítanak az adatbázisok. Mivel nagyon kényes, titkos adatok tárolására szolgálhatnak ezek az adatbázisok, erősen megfontolandó, miképp védjük meg ezeket.
Információk tárolásához vagy visszakereséséhez csatlakozni kell az adatbázishoz, egy érvényes lekérdezést kell küldeni, az eredményt ki kell olvasni, és le kell zárni a kapcsolatot. Manapság ebben a párbeszédben a Structured Query Language (SQL) a leggyakrabban használt lekérdezőnyelv. Figyeld meg, miként lehet SQL lekérdezéseket megbabrálni!
Mint látható, a PHP egymagában, magától nem képes megvédeni az adatbázist. A következő bekezdések célja, hogy betekintést adjanak az alapokba, hogyan kell adatbázisokat elérni és módosítani egy PHP programon belül.
Tartsd észben a következő egyszerű szabályt: tagoltan védekezni. Minél több helyen minél többet teszel a biztonság növeléséért, annál kisebb a valószínűsége, hogy a támadók sikerrel járjanak, és kiteregessék titkos adataidat, vagy visszaéljenek velük. A jó adatbázis- és alkalmazástervezés mindig a legnagyobb félelmek figyelembevételéről ismerszik meg.
Az első lépés mindig az adatbázis létrehozása, hacsak nem egy kívülállóét kell használni. Az adatbázis létrehozásakor az a tulajdonosáé lesz, azé, aki lefuttatta az utasításokat. Általában csak a tulajdonos - esetleg az ún. superuser - jogosult bármiféle az adatbázis elemeit érintő műveletre. Annak érdekében, hogy más felhasználók is hozzáférjenek, jogokat kell nekik biztosítani.
Az alkalmazásoknak soha nem szabad a tulajdonosaként vagy superuserként csatlakozni az adatbázishoz, mert ezek bármilyen utasítást és lekérdezést tetszés szerint futtathatnak, pl. a szerkezeti módosítást (táblák megszüntetése) vagy táblák komplett törlése.
Létre lehet hozni különböző, szigorúan korlátozott jogosultásgú adatbázis- felhasználókat, melyek mindegyike az adatbázis manipulációnak egy-egy különböző nézőpontjáért felelősek. Mindig csak a legszükségesebb jogokat szabad engedélyezni, és el kell kerülni, hogy ugyanazt a felhasználót használjuk szerepeiben egymástól különböző esetekben. Ez azt jelenti, hogy ha a behatoló meg is szerzi valamelyik ilyen minősítést (hitelesítési információt = felhasználói név + jelszó), akkor is csak akkora változást tud okozni, mint az alkalmazás maga.
Nem kell minden feladatfüggő szabályozást a webalkalmazásban (PHP szkriptben) kódolni, ehelyett inkább használd az adatbázis lehetőségeit: view-k (nézetek), trigger-ek, rule-ok (szabályok). Ha a rendszer fejlődik, és más alkalmazásokat is csatlakoztatni kell az adatbázishoz, akkor mindegyiknél újra kellene programozni ezeket a szabályokat. Mindezen felül a triggerek arra is jók, hogy átlátszó módon és automatikusan kezeljenek egyes mezőket az adatbázisban, amelyek gyakran bepillantást adnak abba, hogy mi is történik/történt egy tranzakció közben, vagy nagyon hasznosnak bizonyulhatnak hibakeresés során.
Elképzelhető, hogy SSL-n keresztül szeretnél kapcsolódni az adatbázishoz, hogy a kiszolgáló és ügyfél közti teljes kommunikáció titkosításával növeld a védelmet. Használhatsz ssh-t is erre a célra. Akármelyik is áll, nagyon nehéz lesz a forgalom lehallgatásából információkat kinyerni ezek után.
SSL/SSH az ügyfél és kiszolgáló közt mozgó adatokat védi, és nem védi az adatbázisban tárolt megmaradó adatokat. Az SSL - kapcsolati protokoll.
Mihelyst a támadó közvetlen hozzáférést szerzett az adatbázishoz - megkerülve a webszervert -, a tárolt adatok védtelenné váltak, és visszaélhet velük, ha csak maga az adatbázis nem védi valahogy azokat. Az adatok titkosítása kellőképp enyhíti ezt a veszélyt, de jelenleg nagyon kevés adatbázis kezelő támogatja a titkosítást.
Ez a legkönnyebben saját titkosító csomag írásával oldható meg, amelyet utána a PHP szkriptből el lehet érni. Ebben az esetben a PHP segítséget nyújthat néhány kiterjesztésével, mint például az Mcrypt vagy az Mhash, amelyek nagyon sokféle titkosító algoritmust fednek le. A szkript először titkosítja a tárolni kívánt adatot, majd visszakereséskor visszafejti azokat. Nézd meg a hivatkozott fejezeteket további példákért, hogyan kell a titkosítást végrehajtani.
Olyan teljesen rejtett adatok esetén, amelyeknek nyílt ábrázolásukra nincs szükség, mert nem lesznek kiíratva, a hashelés alkalmazása is meggondolandó. A hashelés jól ismert példája az, hogy a jelszavak helyett, azoknak csak MD5 hash értékét tárolják az adatbzisban. Lásd még: crypt() és md5()!
Sok web fejlesztő nincs tudatában annak, hogy hogyan lehet megbabrálni az SQL utasításokat, ezért az SQL utasításokat megbízható parancsoknak feltételezik. Ez azt jelenti, hogy az SQL lekérdezésekkel ki lehet játszani a hozzáférés szabályozásokat, meg lehet kerülni a szabályos engedélyezési folyamatokat , és néha az SQL lekérdezésekkel a gazdagépen operációs rendszer szintű hozzáférést is lehet létrehozni.
A "közvetlen SQL utasítás befecskendezés" olyan módszer, amellyel a támadó a régi SQL utasításokat módosítja vagy újakat ad hozzájuk annak érdekében, hogy titkos információkhoz jusson hozzá, vagy felülírja azokat, vagy veszélyes rendszer szintű parancsokat futtasson az adatbázis gazdagépén. Ez olyan alkalmazások esetén tehető meg, amelyek a felhasználótól származó adatokból és statikus paraméterekből állítanak össze SQL lekérdezéseket. Sajnos, a következő példák mind megtörtént eseteken alapulnak.
Az, hogy az adatbázishoz superuserként (olyan személyként, aki superusert képes létrehozni) csatlakozott az alkalmazás, és a bevitt adatok ellenőrzésének hiánya odavezethet, hogy a támadó superuser hozzáférést hozhat létre az adatbázishoz.
Példa 5-6. A keresési eredmények lapokra tördelése ... és superuserek létrehozása (PostgreSQL és MySQL)
|
// PostgreSQL 0; insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd) select 'crack', usesysid, 't','t','crack' from pg_shadow where usename='postgres'; -- // vagy MySQL esetén 0; UPDATE user SET Password=PASSWORD('crack') WHERE user='root'; FLUSH PRIVILEGES; |
Megjegyzés: Általános módszer, hogy a -- jellel kényszerítik ki, hogy az SQL elemző figyelmen kívül hagyja a lekérdezésként átadott string fennmaradó részét, mivel ez a megjegyzés szabványos jelölése SQL-ben.
Egy lehetséges módja a jelszavak megszerzésének, hogy kijátszák a kereső oldalak találati listájának lekérdezéseit. A támadónak mindössze annyit kell tennie, hogy végig próbálja melyik elküldött SQL lekérdezésben használt változó nincs megfelelően lekezelve. Ezeket általában egy megelőző űrlapon lehet beállítani, hogy testre szabjuk a SELECT utasítás WHERE, ORDER BY, LIMIT és OFFSET klauzuláit. Ha a használt adatbáziskezelő támogatja a UNION szerkezetet, akkor a támadó esetleg hozzáfűzhet egy teljesen új lekérdezést a már meglevőhöz, hogy kilistázza valamelyik táblában tárolt jelszavakat. Titkosított tárolás erősen ajánlott!
SQL UPDATE parancsok ugyancsak ki vannak téve az adatbázisok elleni támadásoknak. Ezeket az utasításokat is fenyegetik az előzőekben megismert megrövidítő és hozzáfűző technikák. Ám emellett a támadó meghamisíthatja a SET klauzulát is. Ebben az esetben némi séma információval rendelkeznie kell a támadónak, hogy sikerrel járjon. Ezeket az információkat az űrlapváltozók neveiből szerezhetik meg, vagy egyszerűen próbálgatással. Az általánosan használt elnevezések a felhasználói névre és jelszóra nem nagyon különböznek egymástól.
// $uid == "' or uid like'%admin%'; --" $query = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%'; --';"; // $pwd == "hehehe', admin='yes', trusted=100 " $query = "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE ..."; |
Egy ijesztő példa, hogyan lehet az adatbázis gazdagépén operációs rendszerszintű parancsokat futtatni.
$query = "SELECT * FROM products WHERE id LIKE '%a%' exec master..xp_cmdshell 'net user test testpass /ADD'--%'"; $result = mssql_query($query); |
Megjegyzés: A példák némelyike bizonyos adatbáziskezelőhöz kötődik. Ez nem azt jelenti, hogy hasonló támadás elképzelhetetlen más termékkek ellen. Az általad használt adatbázis-kezelő ugyanilyen sérülékeny lehet, akár más módon.
Ellenevetésként felmerülhet, hogy a példák többségében a támadónak rendelkeznie kell valamennyi előzetes információval az adatbázis felépítéséről. Ez igaz, de soha nem lehet tudni, hogy mikor, hol, hogyan szerezhetik meg ezeket, és ha ez megtörtént, az adatbázisod védtelenné válik. A behatolók könnyen hozzájuthatnank a program egy darabjához nyílt forráskódú, vagy olyan nyilvánosan elérhető adatbázis-kezelő programcsomag használatakor, amelyik egy fórum vagy tartalomszolgáltató rendszer része. Ez különösen veszélyes lehet, ha ezek kevéssé átgondoltak és gyengén megtervezettek.
Ezek a támadások alapvetően olyan programoknak a kijátszásán alapulnak, amelyek a védelmet/biztonságot figyelmen kívül hagyva születtek. Soha nem lehet megbízni semmilyen bejövő adatban, főleg ha az a kliens oldalról érkezik, még akkor sem, ha az egy általunk megadott süti (cookie), vagy rejtett mező (hidden input) értéke esetleg egy legördülő lista eleme. Még egy olyan ártatlan lekérdezés, mint ami az első példában látható, katasztrófát okozhat.
Soha ne csatlakozz az adatbázishoz tulajdonosaként vagy superuser-ként. Mindig kevés jogosultsággal rendelkező, testreszabott felhasználókat használj!
Ellenőrizd a bejövő adat típusát, hogy az a vártnak megfelelő-e! A PHP a bevitelt ellenőrző függvények széles körével rendelkezik kezdve a legegyszerűbbektől - pl.: Változókkal kapcsolatos függvények közül is_numeric() vagy a Character Type Functions közül a ctype_digit() - a Perl kompatibilis reguláris kifejezések támogatásáig.
Ha az alkalmazás számot vár, akkor megfontolandó az is_numeric() függvénnyel ellenőrizni a típusát, vagy csendben megváltoztatni a típusát a settype() függvénnyel, vagy szám szerinti ábrázolását használni az sprintf() függvénnyel.
Példa 5-10. A lapozáshoz használt lekérdezés összeállításának biztonságosabb módja
|
Idézőjelek közé kell tenni minden nem szám jellegű, felhasználótól származó adatot, erre használható az addslashes() vagy az addcslashes(). Lásd még az első példát! Ahogy a példák is mutatják, a statikus részbe égetett idézőjelek nem elegendőek, és könnyen kijátszhatók.
Semmilyen adatbázisra jellemző információt - különösen szerkezetit - nem szabad kiírni, ha törik, ha szakad. Lásd még: Hibajelzés és Hibakezelő és naplózó függvények!
Tárolt eljársokat és előre definiált kurzorokat is használhatsz, hogy az adatbázis elérést absztraháld annak érdekében, hogy a felhasználók ne közvetlenül a táblákhoz vagy nézetekhez férjenek hozzá. Ennek a megoldás azonban egyéb hatásai vannak.
Ezeken kívül, hasznot hajthat a lekérdezések naplózása akár a szkripteken belül, akár ha az adatbázis kezelő maga teszi ezt. Nyilvánvalóan ez nem tud megakadályozni egyetlen ártalmas próbálkozást sem, de segítséget nyújthat annak felderítésében, hogy melyik alkalmazás lett kijátszva. A naplózás önmagában nem, csak a benne megjelenő információkon keresztül válik hasznossá: általában a több részlet, hasznosabb.