Mi az SQL Injection és hogyan lehet megakadályozni a PHP alkalmazásokban?

Tehát úgy gondolja, hogy SQL-adatbázisa hatékony és biztonságos az azonnali megsemmisüléstől? Nos, az SQL Injection nem ért egyet!

Igen, azonnali megsemmisítésről beszélünk, mert nem szeretném ezt a cikket a „biztonság szigorítása” és „rosszindulatú hozzáférés megakadályozása” szokásos béna terminológiájával nyitni. Az SQL Injection olyan régi trükk a könyvben, hogy mindenki, minden fejlesztő nagyon jól tud róla, és jól tudja, hogyan lehet megelőzni. Kivéve azt az egy furcsa esetet, amikor megcsúsznak, és az eredmények katasztrofálisak lehetnek.

Ha már tudja, mi az SQL Injection, nyugodtan ugorjon a cikk második felére. De azoknak, akik még csak most jönnek a webfejlesztés területére, és magasabb beosztásról álmodoznak, érdemes némi bemutatkozással.

Mi az SQL Injection?

Az SQL Injection megértésének kulcsa a nevében rejlik: SQL + Injection. Az „injekció” szónak itt nincs semmilyen orvosi konnotációja, inkább az „injekció” ige használata. Ez a két szó együtt azt a gondolatot közvetíti, hogy az SQL-t webalkalmazásba helyezzük.

SQL elhelyezése webalkalmazásba. . . hmmm . . . Egyébként nem ezt csináljuk? Igen, de nem akarjuk, hogy egy támadó irányítsa az adatbázisunkat. Értsük meg ezt egy példa segítségével.

Tegyük fel, hogy egy tipikus PHP-webhelyet készít egy helyi e-kereskedelmi üzlet számára, ezért úgy dönt, hogy hozzáad egy ehhez hasonló kapcsolatfelvételi űrlapot:

<form action="record_message.php" method="POST">
  <label>Your name</label>
  <input type="text" name="name">
  
  <label>Your message</label>
  <textarea name="message" rows="5"></textarea>
  
  <input type="submit" value="Send">
</form>

És tegyük fel, hogy a send_message.php fájl mindent egy adatbázisban tárol, hogy a bolt tulajdonosai később elolvashassák a felhasználói üzeneteket. Valami ehhez hasonló kód lehet:

<?php

$name = $_POST['name'];
$message = $_POST['message'];

// check if this user already has a message
mysqli_query($conn, "SELECT * from messages where name = $name");

// Other code here

Tehát először megpróbálja megnézni, hogy a felhasználónak van-e már olvasatlan üzenete. A SELECT * lekérdezés olyan üzenetekből, ahol név = $név elég egyszerűnek tűnik, igaz?

ROSSZ!

Ártatlanságunkban megnyitottuk az ajtót adatbázisunk azonnali megsemmisítése előtt. Ahhoz, hogy ez megtörténjen, a támadónak teljesítenie kell a következő feltételeket:

  • Az alkalmazás SQL adatbázison fut (ma már szinte minden alkalmazás)
  • Az aktuális adatbázis-kapcsolat „szerkesztési” és „törlési” jogosultsággal rendelkezik az adatbázison
  • A fontos táblázatok nevei kitalálhatók
  Hogyan szerezhetek online technológiai mester- és alapképzést?

A harmadik pont azt jelenti, hogy most, hogy a támadó tudja, hogy Ön egy e-kereskedelmi üzletet üzemeltet, nagy valószínűséggel a rendelési adatokat egy rendelési táblázatban tárolja. Mindezzel felvértezve a támadónak csak ezt kell megadnia névként:

Joe; csonkolja a parancsokat;? Igen Uram! Nézzük meg, mi lesz a lekérdezés, amikor a PHP szkript végrehajtja:

SELECT * FROM üzenetek WHERE név = Joe; csonkolja a rendeléseket;

Rendben, a lekérdezés első részében szintaktikai hiba van (nincs idézőjel a „Joe” körül), de a pontosvessző arra kényszeríti a MySQL-motort, hogy kezdjen el értelmezni egy újat: a rendelések csonkolását. Csak így, egyetlen csapásra a teljes rendeléstörténet eltűnt!

Most, hogy tudja, hogyan működik az SQL Injection, ideje megvizsgálni, hogyan lehet megállítani. A sikeres SQL-befecskendezéshez a következő két feltételnek kell teljesülnie:

  • A PHP szkriptnek módosítási/törlési jogosultsággal kell rendelkeznie az adatbázisban. Azt hiszem, ez minden alkalmazásra igaz, és nem fogja tudni az alkalmazásait írásvédettvé tenni. 🙂 És képzeld el, még ha eltávolítunk minden módosítási jogosultságot, az SQL injekció továbbra is lehetővé teheti valakinek, hogy SELECT lekérdezéseket futtasson, és megtekintse az összes adatbázist, beleértve az érzékeny adatokat is. Más szavakkal, az adatbázis-hozzáférési szint csökkentése nem működik, és az alkalmazásnak mindenképpen szüksége van rá.
  • A felhasználói bevitel feldolgozása folyamatban van. Az SQL injekció csak akkor működhet, ha adatokat fogad el a felhasználóktól. Ismétlem, nem célszerű leállítani az összes bevitelt az alkalmazáshoz csak azért, mert aggódik az SQL injekció miatt.
  • SQL-befecskendezés megakadályozása PHP-ben

    Most, hogy az adatbázis-kapcsolatok, a lekérdezések és a felhasználói bemenetek az élet részét képezik, hogyan akadályozhatjuk meg az SQL-befecskendezést? Szerencsére ez nagyon egyszerű, és kétféleképpen lehet megtenni: 1) megtisztítani a felhasználói bevitelt, és 2) előkészített utasításokat használni.

    A felhasználói bevitel megtisztítása

    Ha régebbi PHP-verziót használ (5.5 vagy régebbi, és ez gyakran előfordul megosztott tárhelyen), akkor bölcs dolog, ha minden felhasználói bevitelt a mysql_real_escape_string() függvényen keresztül futtat. Alapvetően az, hogy eltávolítja az összes speciális karaktert egy karakterláncból, így elveszítik jelentésüket, amikor az adatbázis használja.

    Például, ha van egy karakterlánca, mint például az I’m a string, akkor az egy idézőjel karaktert (‘) használhatja a támadó a létrehozandó adatbázis-lekérdezés manipulálására, és SQL-befecskendezést okozhat. A mysql_real_escape_string()-en keresztül futtatva az I’m egy karakterláncot produkálja, amely egy fordított törtjelet ad az egyetlen idézőjelhez, elkerülve azt. Ennek eredményeként a teljes karakterlánc ártalmatlan karakterláncként kerül át az adatbázisba, ahelyett, hogy részt vehetne a lekérdezés manipulálásában.

      6 Saját üzemeltetésű, könnyű eszközök a szerverek figyeléséhez

    Ennek a megközelítésnek van egy hátránya: ez egy nagyon-nagyon régi technika, amely együtt jár a PHP adatbázis-hozzáférés régebbi formáival. A PHP 7-től kezdve ez a funkció már nem is létezik, így eljutunk a következő megoldáshoz.

    Használjon előkészített nyilatkozatokat

    Az elkészített kimutatások biztonságosabbá és megbízhatóbbá teszik az adatbázis-lekérdezéseket. Az ötlet az, hogy a nyers lekérdezés adatbázisba küldése helyett először elmondjuk az adatbázisnak a küldendő lekérdezés szerkezetét. Ezt értjük nyilatkozat „készítése” alatt. Miután elkészítettük az utasítást, paraméterezett bemenetként adjuk át az információkat, hogy az adatbázis „ki tudja tölteni a hiányosságokat” azáltal, hogy csatlakoztatja a bemeneteket a korábban elküldött lekérdezési struktúrához. Ez elveszi a bemenetek esetleges különleges teljesítményét, ami azt eredményezi, hogy csupán változóként (vagy hasznos terhelésként) kezelik őket az egész folyamatban. Így néznek ki az elkészített nyilatkozatok:

    <?php
    $servername = "localhost";
    $username = "username";
    $password = "password";
    $dbname = "myDB";
    
    // Create connection
    $conn = new mysqli($servername, $username, $password, $dbname);
    
    // Check connection
    if ($conn->connect_error) {
        die("Connection failed: " . $conn->connect_error);
    }
    
    // prepare and bind
    $stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
    $stmt->bind_param("sss", $firstname, $lastname, $email);
    
    // set parameters and execute
    $firstname = "John";
    $lastname = "Doe";
    $email = "[email protected]";
    $stmt->execute();
    
    $firstname = "Mary";
    $lastname = "Moe";
    $email = "[email protected]";
    $stmt->execute();
    
    $firstname = "Julie";
    $lastname = "Dooley";
    $email = "[email protected]";
    $stmt->execute();
    
    echo "New records created successfully";
    
    $stmt->close();
    $conn->close();
    ?>

    Tudom, hogy a folyamat szükségtelenül bonyolultnak hangzik, ha még nem ismeri az előkészített nyilatkozatokat, de a koncepció megéri az erőfeszítést. Itt van szép bevezetés hozzá.

    Azok számára, akik már ismerik a PHP PDO kiterjesztését és használják előkészített utasítások készítésére, adok egy kis tanácsot.

    Figyelmeztetés: Legyen óvatos az OEM beállításakor

    Ha PDO-t használunk adatbázis-hozzáféréshez, hamis biztonságérzetbe kerülhetünk. „Ah, nos, én az OEM-et használom. Most már nem kell másra gondolnom” – általában így megy a gondolkodásunk. Igaz, hogy a PDO (vagy MySQLi által készített utasítások) elegendő mindenféle SQL injekciós támadás megelőzésére, de óvatosnak kell lenni a beállításánál. Gyakori, hogy az oktatóanyagokból vagy a korábbi projektekből származó kódot másolja és illessze be, és továbblép, de ez a beállítás mindent visszavonhat:

    $dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

    Ez a beállítás azt utasítja a PDO-ra, hogy emulálja az előkészített utasításokat ahelyett, hogy ténylegesen használja az adatbázis előkészített utasításait. Következésképpen a PHP egyszerű lekérdezési karakterláncokat küld az adatbázisnak, még akkor is, ha a kód úgy tűnik, hogy előkészített utasításokat hoz létre, paramétereket állít be, és minden. Más szóval, ugyanúgy ki van téve az SQL-befecskendezésnek, mint korábban. 🙂

      9 legjobb ASN online keresés, szkriptek és API

    A megoldás egyszerű: győződjön meg arról, hogy ez az emuláció hamisra van állítva.

    $dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

    A PHP szkript most arra kényszerül, hogy adatbázis szinten előkészített utasításokat használjon, megakadályozva mindenféle SQL-injektálást.

    A WAF használatának megelőzése

    Tudja, hogy WAF (webes alkalmazás tűzfal) használatával is megvédheti a webes alkalmazásokat az SQL injekciótól?

    Nos, nem csak az SQL-befecskendezés, hanem sok más 7. rétegű sebezhetőség, mint például a helyek közötti szkriptelés, a hibás hitelesítés, a helyek közötti hamisítás, az adatok nyilvánosságra hozatala stb. Az alábbiak szerint használhatja az önkiszolgálót, például a Mod Security-t, vagy a felhőalapút.

    SQL injekció és modern PHP keretrendszerek

    Az SQL-befecskendezés olyan gyakori, olyan egyszerű, frusztráló és veszélyes, hogy minden modern PHP webes keretrendszerbe beépítve vannak ellenintézkedések. A WordPressben például van a $wpdb->prepare() függvény, míg ha MVC-keretrendszert használunk, az elvégzi helyettünk az összes piszkos munkát, és még csak gondolnia sem kell az SQL-befecskendezés megakadályozására. Kicsit bosszantó, hogy a WordPress-ben kifejezetten kijelentéseket kell készíteni, de hát WordPressről beszélünk. 🙂

    Amúgy a lényeg az, hogy a webfejlesztők modern fajtájának nem kell az SQL-befecskendezésre gondolnia, és ebből kifolyólag nem is ismeri a lehetőséget. Mint ilyenek, még ha nyitva hagynak is egy hátsó ajtót az alkalmazásukban (talán ez egy $_GET lekérdezési paraméter, és a régi szokások, amikor egy piszkos lekérdezést indítanak el), az eredmények katasztrofálisak lehetnek. Ezért mindig jobb, ha időt szán arra, hogy mélyebbre merüljön az alapokba.

    Következtetés

    Az SQL Injection egy nagyon csúnya támadás egy webalkalmazás ellen, de könnyen elkerülhető. Amint ebben a cikkben láttuk, csak óvatosnak kell lenni a felhasználói bevitel feldolgozásakor (mellesleg, az SQL-injekció nem az egyetlen fenyegetés, amelyet a felhasználói bevitel kezelése jelent), és az adatbázis lekérdezése nem más, mint az. Ennek ellenére nem mindig dolgozunk a webes keretrendszer biztonságán, ezért jobb, ha tisztában vagyunk az ilyen típusú támadásokkal, és nem dőlünk be neki.