Preventing SQL injection in PHP (and other sebezhetőségek)
ha már körül Webfejlesztés egy darabig, akkor már szinte biztosan hallott a “SQL injection” és néhány rémisztő történeteket róla.
a PHP, mint sok más nyelv, nem immunis az ilyen típusú fenyegetésekre, amelyek valóban nagyon veszélyesek lehetnek. De szerencsére a webhelyek védelme az SQL-befecskendezéssel és más hasonló fenyegetésekkel szemben kézzelfogható lépéseket tehet.
ebben a bejegyzésben megtudhatja, mi az SQL injekció, milyen következményekkel jár a sikeres támadás, hogyan használhatja ki a támadó a sebezhető PHP kódot, mit tehet annak megakadályozása érdekében, és milyen eszközökkel észlelheti a kód azon részeit, amelyek ilyen fenyegetésnek lehetnek kitéve. Ezenkívül megismerhet néhány más gyakori sebezhetőséget, amelyekkel tisztában kell lennie a biztonságosabb alkalmazások létrehozása érdekében.
hogyan működik az SQL injekció?
az első dolog, amit tudnia kell, hogy megvédje a kódot az SQL injekciótól, hogy megértse, hogyan lehet kihasználni a támadó.
az exploit ötlete meglehetősen egyszerű: a támadó rosszindulatú SQL kódot futtat az adatbázisban az alkalmazáson keresztül.
hogyan lehet ezt elérni? Visszaélve az alkalmazás bemenetek.
nézzünk egy egyszerű példát. Tegyük fel, hogy van egy alkalmazás, amely a felhasználónevek listáját mutatja, ahol minden felhasználónak van egy linkje az adataikhoz:
, majd a user_details.php
fájlban ez van:
<?php $sql = "SELECT * FROM user WHERE id = ".$_GET;
ha a felhasználó a várt módon cselekszik, és rákattint valamelyik felhasználó nevére, a kérés URL-címe így fog kinézni: http://domain.com/user_details.php?id=8
.
Szóval, mi a baj vele?
a $sql
értéke a "SELECT * FROM user WHERE id = 8"
karakterlánc lesz, és a lekérdezés csak finom lesz.
a probléma a mondat dőlt betűs részében van: ha a felhasználó a várt módon cselekszik, és rákattint valamelyik felhasználó nevére, a kérés URL-címe Így fog kinézni:
http://domain.com/user_details.php?id=8
.
mi történik, ha a felhasználó nem a várt módon jár el?
mi lenne, ha valaki egy URL-t gyártana, amely nem egy számot küld azonosítóként?
gyártott URL-ek
ha az URL valami hasonló lenne http://domain.com/user_details.php?id=Peanuts
, az eredményül kapott SQL-karakterlánc a következő lenne: "SELECT * FROM user WHERE id = Peanuts"
.
ebben az esetben a probléma a felhasználó számára lenne. A lekérdezés egyszerűen sikertelen lenne, és a legrosszabb esetben a felhasználó valamilyen furcsa üzenetet látna.
de mi lenne, ha az URL inkább így nézne ki:
http://domain.com/user_details.php?id=1+OR+1%3B
? (Ez egyébként a urlencode('1 OR 1;')
eredménye.)
ebben az esetben a kapott SQL "SELECT * FROM user WHERE id = 1 OR 1;"
lenne. Ez azt jelenti, hogy a lekérdezés eredménye az adatbázis minden felhasználója lenne, nem csak az a felhasználó, akinek az azonosítója 1. Jaj.
de ez még mindig nem túl rossz, igaz?
most próbáljunk ki egy szélsőségesebb példát.
tegyük fel, hogy az URL valami hasonló volt
http://domain.com/user_details.php?id=1%27%3B+DROP+TABLE+users%3B--+
, ami lefordítja id=1; DROP TABLE users;--
, hatékonyan előállítva ezt az SQL parancsot:
"SELECT * FROM users WHERE id = 1; DROP TABLE users;-- "
. És ha futtatod ezt a lekérdezést … nagy jaj.
abban az esetben, ha nem 100% – ban ismeri az SQL szintaxist, DROP TABLE
teljesen törli a táblázatot. Olyan lenne, mintha soha nem is létezett volna.
természetesen egy friss biztonsági mentés segítene csökkenteni a károkat, de mégis. Ez rossz.
ha többet szeretne megtudni erről a fajta támadásról, itt van egy nagyszerű bejegyzés.
de azt hiszem, ez elég rossz hír. Térjünk át egy vidámabb helyre: hogyan lehet megakadályozni az ilyen típusú SQL-injekciókat a PHP-alkalmazásokban.
hogyan lehet megakadályozni az SQL injekciókat a PHP-ben
mint látta, a probléma az, amikor a támadó képes futtatni az SQL kódot anélkül, hogy tudatában lenne annak.
ez a helyzet annak a következménye, hogy a lekérdezéseket menet közben hozzák létre karakterlánc-összefűzéssel, beleértve a külső bemenetekről gyűjtött adatokat (általában felhasználói bemenetek, de más külső források is, például fájlok, API-hívásokra adott válaszok stb.).
tehát a kód biztonsági résének megakadályozása érdekében meg kell oldania a lehetséges problémák forrásait.
a bemenetek érvényesítése
a fenti példában szereplő biztonsági rés kijavításának egyszerű módja annak ellenőrzése, hogy az ID paraméter valóban elvárható-e tőle (azaz., pozitív egész szám):
az ilyen érvényesítés másik módja a PHP beépített szűrőinek kihasználása:
<?php
$id = filter_input( INPUT_GET, 'id', FILTER_VALIDATE_INT);
tehát a bemenetek érvényesítésével megakadályozhatja, hogy a támadók rosszindulatú kódot hajtsanak végre a sajátja mellett.
nincs egyszerű módja annak, hogy megakadályozzák őket a próbálkozásban, de amíg a kísérleteik nem sikeresek, akkor jó menni.
előkészített utasítások használata
egy másik módja annak, hogy megvédje kódját az SQL injekciók ellen, az előkészített utasítások használata. Az elkészített utasítások előre lefordított SQL parancsok.
használhatók egy adott adatbázis-hozzáférési könyvtárral (például a mysqli-vel) vagy az általánosabb OEM könyvtárral.
vessünk egy pillantást egy példára a mysqli használatával:
ha kipróbálja ezt a kódot (természetesen az adatbázis hitelesítő adatainak cseréje), látni fogja, hogy ha egy legális URL-en keresztül fér hozzá, mint például a http://domain.com/user_details.php?id=1
, akkor ugyanazt az eredményt kapja, mint a fenti rosszindulatú URL-ek (ami az 1.azonosítóhoz társított felhasználói információ, semmi más).
ha inkább OEM-et használ, a kód inkább így fog kinézni:
nem igazán nagy különbség, igaz?
összefoglalva, az elkészített nyilatkozatok nagyszerű módot kínálnak az SQL injekciók megelőzésére. Ezenkívül általában jobban teljesítenek, mint az on-the-fly SQL.
most, hogy ismeri az eszközt, csak használnia kell.
hogyan lehet felismerni az SQL injekciókkal szemben sebezhető PHP kódot
Miután megtalálta a biztonsági rést, a javítása nem igazán bonyolult. De hogyan találja meg az ilyen típusú sebezhetőségeket a kódjában?
Nos, ha szerencséd van, és a kódbázisod elég kicsi, a kód egyszerű áttekintése megteszi.
de ha az alkalmazás közepes méretű vagy nagy, akkor további segítségre van szüksége.
az egyik nagyon népszerű (és nyílt forráskódú) eszköz, amelyet erre a célra használhatsz, az sqlmap, egy egyszerű Python szkript, amely megpróbál megtámadni bármilyen URL-t, amelyet betáplálsz hozzá, és jelentést készít az eredményekről.
itt van egy Minta kimenet egy PHP-alapú webhelyem ellen:
egy másik nyílt forráskódú eszköz, amelyet használhat, az Arachni, egy kifinomultabb eszköz, amely webes grafikus felületet tartalmaz, és Ruby-ban íródott.
itt egy Minta kimenet fut át a CLI:
a kódellenőrzésen és-tesztelésen túl fontolóra kell vennie egy RASP-megoldás, például az sqreen használatát a termelési környezetek figyelemmel kíséréséhez, hogy megállítsa az olyan exploitok végrehajtását, amelyek átjutnak a felülvizsgálati és tesztelési szakaszokon.
milyen egyéb biztonsági réseket kell tudnia?
Kódinjekciók
az SQL injection csak egy tagja a sebezhetőségek nagyobb családjának: kódinjekció.
a különböző kódinjekciós technikák mögött álló ötlet mindig ugyanaz. Az egész egy ártatlan alkalmazás becsapásáról szól rosszindulatú kód futtatására.
ezt többféle módon lehet megtenni.
a PHP-ben vannak olyan funkciók, amelyek egyszerűen parancsokat adnak ki közvetlenül az operációs rendszerbe, például a exec
funkció.
Íme egy egyszerű példa arra, hogyan lehet ezt kihasználni:
<?php
exec( 'cp uploads/'.$_GET.' /secure/');
ha valaki http://domain.com/exec.php?file=a.txt%3B+rm+-Rf+%2A+%23
kérést kérne, az eredmény ennek a parancsnak a végrehajtása lenne:
cp uploads/a.txt; rm -Rf * # /secure/
ez pedig megsemmisítheti az egész lemezt a szerveren—az érvénytelenített bemenet újra lecsap!
Cross-site scripting
egy másik nagyon gyakori biztonsági rés a cross-site scripting (XSS). Ebben az esetben az áldozat az Ön webhelyét látogató felhasználó.
XSS az, amikor a támadó rosszindulatú JavaScript kódot injektál egy szokásos HTML űrlapba, amelyet később egy másik felhasználó böngészője renderel.
nézd meg ezt a PHP szkriptet:
<?php
echo "<html><p>Hello {$_GET}!</p></html>";
nyilvánvaló, hogy itt egy egyszerű oldal létrehozása a cél, amely üdvözli a felhasználót.
de amikor a rosszfiúk eljutnak hozzá, mi akadályozza meg őket abban, hogy kérjenek
http://domain.com/greet.php?name=Mauro%3Cscript+language%3D%22javascript%22%3Ealert%28%22You+are+hacked%21%22%29%3B%3C%2Fscript%3E
? Ez a kérés a következő HTML-t eredményezi kimenetként:
<html><p>Hello Mauro<script language="javascript">alert("You are hacked!");</script></p></html>
ez egyértelműen nagyon naiv kizsákmányolás-elvégre mi a baj, ha valakinek egyszerű ” feltörtek!”üzenet? De a dolgok elég komolyra fordulhatnak egy kis képzelőerővel. Csak ne feledje, hogy a szerverhez intézett minden kérés tartalmazza a munkamenet-sütit, így ez a biztonsági rés lehet a tettes a felhasználók munkameneteinek eltérítése mögött.
aztán megint, míg a problémák csúnyának tűnnek, a javítások nagyon egyszerűek: az egész az érvényesítésről és a fertőtlenítésről szól.
az XSS esetében egy egyszerű hívás htmlentities
– ra a tényleges kimenet előállítása előtt.
javítás vs.biztonsági rések észlelése
mint ebben a bejegyzésben látta, a biztonsági rések—mind az SQL injekciók, mind más típusok—kijavítása nem a fő kihívás. Felismerve, hogy a kód sebezhető, és hol lehet kihasználni, az. Van egy pár lehetőség itt:
- nagyon erős kódolási fegyelem
- tesztelje az alkalmazások alaposan biztonsági kérdések
- tőkeáttétel felügyeleti és védelmi eszközök megelőzése hasznosítja, és kap riasztásokat időben
melyiket választja rajtad múlik. Az a fontos, hogy figyeljünk ezekre a problémákra, és cselekedjünk ezen ismeretek alapján. Maradj biztonságban.