Röviden: Az SQL-injectiont PHP-ban megbízhatóan úgy előzi meg, hogy az értékeket soha nem építi be az SQL-szövegbe, hanem mindig Prepared Statementeken keresztül, helyőrzőkkel adja át. Az oszlop- és táblanevekre, amelyeket nem lehet helyőrzőként átadni, rögzített engedélylista érvényes. Emellett egy minimális jogokkal rendelkező adatbázis-felhasználó korlátozza a kárt arra az esetre, ha mégis marad rés.
Miért nem szabad szem elől téveszteni az SQL-injectiont a PHP2Shell után
2026 októberében sok minden a PHP2Shell körül forog, a PHP-értelmezőben lévő két kritikus rés körül, amelyeket 2026. 10. 21. óta a PHP 8.5.12, 8.4.27, 8.3.36 és 8.2.35 verzió javít. Sok csapat ezekben a napokban frissítette a szervereket és indította újra a folyamatokat. Ez helyes és sürgős.
Egy aktuális PHP-állapot azonban semmit sem mond a saját kódról. Az SQL-injection évek óta a webalkalmazások leggyakoribb sebezhetőségei közé tartozik, és az OWASP Top 10 az Injection kategória alatt tartja számon. Aki úgyis biztonsági vizsgálatokat tervez, az alkalmazása adatbázis-hozzáféréseit is nézze át egyúttal.
A jó hír: az SQL-injection egyike azoknak a ritka sebezhetőségeknek, amelyeket egy egyszerű, következetesen alkalmazott szabállyal szinte teljesen ki lehet zárni.
Hogyan keletkezik az SQL-injection PHP-ban?
Az SQL-injection akkor keletkezik, ha egy alkalmazás a felhasználói bemeneteket egy SQL-parancs részeként kezeli, nem pusztán adatként. Tipikus egy lekérdezés összeállítása szövegrészekből és változókból. Ha egy látogató meghatározhatja egy ilyen változó tartalmát, bizonyos körülmények között megváltoztathatja a parancs jelentését.
A következmények az idegen ügyféladatok kiolvasásától az árak vagy jogok módosításán át egész táblák törléséig terjednek. Az adatbázistól és a jogoktól függően egy rés az adatbázison túli hozzáféréseket is lehetővé tehet. Ha személyes adatok érintettek, a GDPR szerinti bejelentési kötelezettség keletkezhet.
A bemenetek nem csak az űrlapmezők. Az URL-paraméterek, a cookie-k, a HTTP-fejlécek, a feltöltött fájlok, az idegen interfészek válaszai és a saját adatbázisból már tárolt adatok is megbízhatatlannak számítanak.
Prepared Statementek PDO-val és mysqli-vel
A Prepared Statementek az SQL-parancsot és az értékeket külön küldik az adatbázisnak. Az értékeket soha nem értelmezik a parancs részeként. PDO-val egy biztonságos lekérdezés így néz ki:
<?php
$pdo = new PDO($dsn, $benutzer, $passwort, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$stmt = $pdo->prepare('SELECT id, name, email FROM kunden WHERE id = :id AND status = :status');
$stmt->execute([
'id' => (int) $_GET['id'],
'status' => 'aktiv',
]);
$kunde = $stmt->fetch();
A PDO::ATTR_EMULATE_PREPARES => false opció gondoskodik arról, hogy az adatbázis valódi Prepared Statementeket használjon, amennyiben a meghajtó támogatja. A mysqli-vel ugyanez az elv érvényes:
<?php
$stmt = $mysqli->prepare('SELECT id, name FROM produkte WHERE kategorie = ? AND preis <= ?');
$stmt->bind_param('sd', $kategorie, $maxPreis);
$stmt->execute();
$ergebnis = $stmt->get_result();
Az IN (...) listák is biztonságosan megoldhatók, ha minden értékhez külön helyőrzőt hoz létre, és az értékeket külön adja át.
<?php
$ids = array_map('intval', $ausgewaehlteIds);
$platzhalter = implode(',', array_fill(0, count($ids), '?'));
$stmt = $pdo->prepare("SELECT id, name FROM produkte WHERE id IN ($platzhalter)");
$stmt->execute($ids);
Különleges esetek: oszlopnevek, rendezés és nyers SQL
A helyőrzők csak értékekre működnek, táblanevekre, oszlopnevekre vagy rendezési irányokra nem. A gyakorlatban éppen itt keletkezik sok rés, például rendezhető táblázatoknál vagy szűrőknél. A megoldás az engedélyezett értékek rögzített listája:
<?php
$erlaubteSpalten = ['name' => 'name', 'preis' => 'preis', 'datum' => 'erstellt_am'];
$spalte = $erlaubteSpalten[$_GET['sort'] ?? ''] ?? 'name';
$richtung = (($_GET['dir'] ?? '') === 'desc') ? 'DESC' : 'ASC';
$stmt = $pdo->query("SELECT id, name, preis FROM produkte ORDER BY $spalte $richtung");
A keretrendszerek és a CMS-ek saját eszközöket hoznak magukkal. A WordPressben a $wpdb->prepare() veszi át ezt a feladatot, a Laravelben és a Symfonyban a Query Builderek és az ORM-ek. Amint ott nyers SQL-t használ, például a nevükben „raw“ szót tartalmazó függvényeken keresztül, ismét az ebben a szakaszban leírt szabályok érvényesek. A Laravelről a Laravel-alkalmazások védelme oldalon talál tudnivalókat.
Célzottan keressen a kódban olyan SQL-szövegeket, amelyekbe változókat illesztenek. Egyszerű kezdet a query( és a ->raw keresése a $_GET, $_POST vagy $_REQUEST kombinációjában.
SQL-injection felderítése a meglévő kódban
Új szoftvernél a szabály az elejétől fogva betartható. Nehezebb ez az évek alatt kialakult alkalmazásoknál. Ott megéri módszeresen keresni azokat a helyeket, ahol az SQL-szövegeket változókkal állítják össze:
# SQL-parancsok, amelyekbe változókat illesztenek
grep -rnE "(SELECT|INSERT|UPDATE|DELETE)[^;]*\\\$[a-zA-Z_]" src/ --include="*.php" | head -50
# Kérésadatok közvetlen használata lekérdező függvényekben
grep -rnE "(query|exec|raw)\s*\(.*\\\$_(GET|POST|REQUEST|COOKIE)" src/ --include="*.php"
Nem minden találat rés, például ha a változó csak egy rögzített táblanevet tartalmaz. Minden lelőhelyet azonban egyszer tudatosan ellenőrizni kell, és szükség esetén Prepared Statementekre kell átállítani. A statikus kódelemző eszközök segítenek az ilyen minták tartós felismerésében a build-pipeline-ban, hogy az új hibák ne kerüljenek élesbe.
A kár korlátozása: jogok, hibaüzenetek, ellenőrzés
A biztonságos kód az első védvonal. További szintek korlátozzák a kárt arra az esetre, ha mégis átnéznek egy rést:
- Minimális jogok: A weboldal adatbázis-felhasználójának többnyire csak
SELECT,INSERT,UPDATEésDELETEjogra van szüksége a saját adatbázisán, nem kell joga felhasználók létrehozásához vagy más adatbázisok eléréséhez. - Ne kerüljenek hibaüzenetek kifelé:
display_errors = Offa php.ini-ben. Az adatbázishibák a naplóba valók, nem a böngészőbe. - Alkalmazásonkénti elválasztás: Minden weboldal saját adatbázis-felhasználót kap.
- A függőségek legyenek naprakészek: Rések a könyvtárakban is lehetnek, ahogy a composer audit útmutató mutatja.
- Ellenőriztesse: A kódellenőrzés és az automatizált elemzés sok helyet megtalál, a penetrációs teszt megmutatja, mi érhető el ténylegesen kívülről.
-- Példa: adatbázis-felhasználó csak a szükséges jogokkal
CREATE USER 'shop_app'@'localhost' IDENTIFIED BY '<starkes Passwort>';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'shop_app'@'localhost';
Egy rokon sebezhetőség az adatok böngészőben való megjelenítését érinti. Hogyan védheti ezt, azt a A Cross-Site Scripting megelőzése PHP-ban útmutató magyarázza.
Hogyan segít konkrétan a PHP SOS?
- Módszeresen ellenőrizzük az alkalmazása adatbázis-hozzáféréseit nem biztonságos lekérdezések szempontjából.
- A leleteket az elérhetőség és az érintett adatok szerint rangsoroljuk, és konkrét javításokat mutatunk.
- Az adatbázisjogokat és a hibakezelést biztonságos szintre állítjuk.
- A javítás után újra tesztelünk, és dokumentáljuk az eredményt.
Egy független ellenőrzést a Biztonsági audit és penetrációs teszt szolgáltatás ír le.
Gyakori kérdések
Elég a bemeneteket az addslashes-sel vagy a real_escape_string-gel kezelni?
Nem, ez hibára hajlamos. Az escape-elés a karakterkészlettől és a környezettől függ, és könnyen elfelejtik egy helyen. A Prepared Statementek elvileg elválasztják a parancsot és az adatot, ezért ez az ajánlott út.
Automatikusan véd egy ORM az SQL-injection ellen?
Nagyrészt, amíg az ORM lekérdező függvényeit használja. Amint nyers SQL-t ír, vagy értékeket illeszt rendezésekbe és oszlopnevekbe, ugyanazok a szabályok érvényesek, mint ORM nélkül.
Segít egy Web Application Firewall az SQL-injection ellen?
Egy WAF elfoghatja az ismert mintákat, és időt nyerhet. Biztonságos kódot azonban nem pótol, mert a kérésadatok alapján felállított szabályok megkerülhetők, és időnként jogos bemeneteket is eltalálnak.
A PHP2Shell elleni PHP-frissítés az SQL-injection réseket is lezárja?
Nem. A PHP2Shell a PHP-értelmezőt érinti. Az SQL-injection az alkalmazáskód hibája, és fennmarad, bármelyik PHP-verzió fut.