Stručne: SQL injection v PHP spoľahlivo zabránite tak, že hodnoty nikdy nevkladáte do textov SQL, ale vždy ich odovzdávate cez Prepared Statements so zástupnými znakmi. Pre názvy stĺpcov a tabuliek, ktoré sa ako zástupné znaky odovzdať nedajú, platí pevný whitelist. Databázový používateľ s minimálnymi právami navyše obmedzí škodu, ak nejaká zraniteľnosť predsa zostane.
Prečo SQL injection nesmie po PHP2Shell zísť z očí
V októbri 2026 sa mnohé točí okolo PHP2Shell, dvoch kritických zraniteľností v interpreteri PHP, opravených od 21. 10. 2026 v PHP 8.5.12, 8.4.27, 8.3.36 a 8.2.35. Mnohé tímy v týchto dňoch aktualizovali servery a reštartovali procesy. To je správne a naliehavé.
Aktuálny stav PHP však nehovorí nič o vlastnom kóde. SQL injection patrí už roky k najčastejším slabinám webových aplikácií a v OWASP Top 10 sa vedie v kategórii Injection. Kto práve tak či tak plánuje bezpečnostné kontroly, mal by sa zároveň pozrieť na prístupy k databáze vo vlastnej aplikácii.
Dobrá správa: SQL injection je jednou z mála slabín, ktorú možno takmer úplne vylúčiť jednoduchým, dôsledne uplatňovaným pravidlom.
Ako vzniká SQL injection v PHP?
SQL injection vzniká, keď aplikácia spracúva vstupy používateľa ako súčasť príkazu SQL namiesto čistých údajov. Typické je skladanie dotazu z textových stavebných prvkov a premenných. Ak môže návštevník určiť obsah takejto premennej, môže za určitých okolností zmeniť význam príkazu.
Následky siahajú od vyčítania cudzích údajov zákazníkov cez zmenu cien alebo práv až po zmazanie celých tabuliek. Podľa databázy a práv môže zraniteľnosť umožniť aj prístupy mimo databázy. Ak sú dotknuté osobné údaje, môže vzniknúť oznamovacia povinnosť podľa GDPR.
Vstupmi pritom nie sú len polia formulárov. Za nedôveryhodné sa považujú aj parametre URL, cookies, hlavičky HTTP, nahrané súbory, odpovede cudzích rozhraní a už uložené údaje z vlastnej databázy.
Prepared Statements s PDO a mysqli
Prepared Statements posielajú príkaz SQL a hodnoty databáze oddelene. Hodnoty sa nikdy nevykladajú ako súčasť príkazu. S PDO vyzerá bezpečný dotaz takto:
<?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();
Voľba PDO::ATTR_EMULATE_PREPARES => false zaisťuje, že databáza používa skutočné Prepared Statements, pokiaľ to ovládač podporuje. S mysqli platí ten istý princíp:
<?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();
Aj zoznamy pre IN (...) sa dajú bezpečne zobraziť tak, že pre každú hodnotu vytvoríte vlastný zástupný znak a hodnoty odovzdáte oddelene.
<?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);
Osobitné prípady: názvy stĺpcov, zoradenie a surové SQL
Zástupné znaky fungujú len pre hodnoty, nie pre názvy tabuliek, názvy stĺpcov ani smery zoradenia. Práve tu v praxi vzniká veľa zraniteľností, napríklad pri zoraditeľných tabuľkách alebo filtroch. Riešením je pevný zoznam povolených hodnôt:
<?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");
Frameworky a CMS prinášajú vlastné nástroje. Vo WordPresse túto úlohu preberá $wpdb->prepare(), v Laraveli a Symfony query buildery a ORM. Len čo tam použijete surové SQL, napríklad cez funkcie s „raw“ v názve, platia opäť pravidlá tohto oddielu. Pokyny k Laraveli nájdete na stránke Zabezpečiť aplikácie Laravel.
Hľadajte v kóde cielene texty SQL, do ktorých sa vkladajú premenné. Jednoduchým začiatkom je hľadanie query( a ->raw v kombinácii s $_GET, $_POST alebo $_REQUEST.
Vypátrať SQL injection v existujúcom kóde
Pri novom softvéri sa pravidlo dá dodržať od začiatku. Ťažšie je to pri aplikáciách, ktoré rástli roky. Tam sa oplatí systematické hľadanie miest, kde sa texty SQL skladajú s premennými:
# Príkazy SQL, do ktorých sa vkladajú premenné
grep -rnE "(SELECT|INSERT|UPDATE|DELETE)[^;]*\\\$[a-zA-Z_]" src/ --include="*.php" | head -50
# Priame použitie údajov požiadavky v dotazovacích funkciách
grep -rnE "(query|exec|raw)\s*\(.*\\\$_(GET|POST|REQUEST|COOKIE)" src/ --include="*.php"
Nie každý nález je zraniteľnosť, napríklad ak premenná obsahuje iba pevný názov tabuľky. Každé miesto nálezu by sa však malo raz vedome preveriť a v prípade potreby prestaviť na Prepared Statements. Nástroje statickej analýzy kódu pomáhajú takéto vzory trvalo rozpoznávať v build pipeline, takže nové chyby sa vôbec nedostanú do ostrej prevádzky.
Obmedziť škodu: práva, chybové hlásenia, kontrola
Bezpečný kód je prvá obranná línia. Ďalšie úrovne obmedzujú škodu, ak sa zraniteľnosť predsa prehliadne:
- Minimálne práva: Databázový používateľ webu zvyčajne potrebuje iba
SELECT,INSERT,UPDATEaDELETEna vlastnú databázu, žiadne práva na zakladanie používateľov ani na prístup k iným databázam. - Žiadne chybové hlásenia smerom von:
display_errors = Offv php.ini. Chyby databázy patria do logu, nie do prehliadača. - Oddelenie podľa aplikácie: Každý web dostane vlastného databázového používateľa.
- Udržiavať závislosti aktuálne: Zraniteľnosti sú aj v knižniciach, ako ukazuje príručka composer audit.
- Nechať skontrolovať: Code review a automatizovaná analýza nájdu mnohé miesta, penetračný test ukáže, čo je zvonku skutočne dosiahnuteľné.
-- Príklad: databázový používateľ iba s potrebnými právami
CREATE USER 'shop_app'@'localhost' IDENTIFIED BY '<starkes Passwort>';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'shop_app'@'localhost';
Príbuzná slabina sa týka výstupu údajov v prehliadači. Ako ju zabezpečiť, vysvetľuje príručka Zabrániť cross-site scripting v PHP.
Ako konkrétne pomáha PHP SOS?
- Prístupy k databáze vašej aplikácie systematicky kontrolujeme na nebezpečné dotazy.
- Nálezy zoraďujeme podľa dosiahnuteľnosti a dotknutých údajov a ukazujeme konkrétne opravy.
- Práva k databáze a spracovanie chýb nastavíme na bezpečnú úroveň.
- Po oprave znova testujeme a výsledok zdokumentujeme.
Nezávislú kontrolu popisuje služba Bezpečnostný audit a penetračný test.
Časté otázky
Stačí vstupy ošetriť pomocou addslashes alebo real_escape_string?
Nie, je to náchylné na chyby. Escapovanie závisí od znakovej sady a kontextu a ľahko sa niekde zabudne. Prepared Statements zásadne oddeľujú príkaz a údaje, a preto sú odporúčanou cestou.
Chráni ORM automaticky pred SQL injection?
Do veľkej miery, pokiaľ používate dotazovacie funkcie ORM. Len čo píšete surové SQL alebo vkladáte hodnoty do zoradenia a názvov stĺpcov, platia rovnaké pravidlá ako bez ORM.
Pomáha Web Application Firewall proti SQL injection?
WAF môže zachytiť známe vzory a získať čas. Nenahrádza však bezpečný kód, pretože pravidlá založené na údajoch z požiadaviek sa dajú obísť a niekedy zasiahnu legitímne vstupy.
Uzatvorí aktualizácia PHP proti PHP2Shell aj zraniteľnosti SQL injection?
Nie. PHP2Shell sa týka interpretera PHP. SQL injection je chyba v kóde aplikácie a zostáva, bez ohľadu na to, ktorá verzia PHP beží.