Lühidalt: SQL-injektsiooni PHP-s väldite usaldusväärselt, kui ei ehita väärtusi kunagi SQL-teksti sisse, vaid edastate need alati Prepared Statementide kaudu kohatäidetega. Veerge ja tabelinimesid, mida kohatäidetena edastada ei saa, kontrollitakse kindla lubatud nimekirjaga. Lisaks piirab minimaalsete õigustega andmebaasikasutaja kahju, kui mõni auk siiski alles jääb.
Miks SQL-injektsioon ei tohi pärast PHP2Shelli silmist kaduda
- aasta oktoobris keerleb palju PHP2Shelli ümber, kahe kriitilise turvaaugu ümber PHP interpretaatoris, mis on parandatud alates 21.10.2026 versioonides PHP 8.5.12, 8.4.27, 8.3.36 ja 8.2.35. Paljud meeskonnad on neil päevil servereid uuendanud ja protsesse taaskäivitanud. See on õige ja kiireloomuline.
Ajakohane PHP seis ei ütle aga midagi oma koodi kohta. SQL-injektsioon kuulub aastaid veebirakenduste sagedasemate nõrkuste hulka ja seda käsitletakse OWASP Top 10 kategoorias Injection. Kes plaanib niikuinii turvakontrolle, peaks oma rakenduse andmebaasipäringud kohe kaasa vaatama.
Hea uudis: SQL-injektsioon on üks väheseid nõrkusi, mille saab lihtsa, järjekindlalt rakendatud reegliga peaaegu täielikult välistada.
Kuidas SQL-injektsioon PHP-s tekib?
SQL-injektsioon tekib, kui rakendus käsitleb kasutaja sisendit SQL-käsu osana, mitte puhaste andmetena. Tüüpiline on päringu kokkupanek tekstiosadest ja muutujatest. Kui külastaja saab sellise muutuja sisu määrata, saab ta teatud tingimustel muuta käsu tähendust.
Tagajärjed ulatuvad võõraste klientide andmete väljalugemisest hindade või õiguste muutmiseni kuni terveid tabeleid kustutamiseni. Andmebaasist ja õigustest olenevalt võib turvaauk võimaldada juurdepääsu ka väljaspool andmebaasi. Kui on mõjutatud isikuandmed, võib tekkida IKÜM-i alusel teavitamiskohustus.
Sisend ei ole siin ainult vormiväljad. Ka URL-i parameetreid, küpsiseid, HTTP päiseid, üleslaaditud faile, võõraste liideste vastuseid ja juba salvestatud andmeid oma andmebaasist tuleb pidada mitteusaldusväärseks.
Prepared Statements PDO ja mysqli abil
Prepared Statements saadavad SQL-käsu ja väärtused andmebaasile eraldi. Väärtusi ei tõlgendata kunagi käsu osana. PDO-ga näeb turvaline päring välja nii:
<?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();
Suvand PDO::ATTR_EMULATE_PREPARES => false tagab, et andmebaas kasutab päris Prepared Statemente, kuivõrd draiver seda toetab. mysqli-ga kehtib sama põhimõte:
<?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();
Ka IN (...) loendeid saab turvaliselt kujutada, luues iga väärtuse jaoks oma kohatäite ja edastades väärtused eraldi.
<?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);
Erijuhtumid: veerunimed, sorteerimine ja toores SQL
Kohatäited toimivad ainult väärtuste puhul, mitte tabelinimede, veerunimede ega sorteerimissuundade jaoks. Just siin tekib praktikas palju auke, näiteks sorteeritavate tabelite või filtrite juures. Lahendus on lubatud väärtuste kindel loend:
<?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");
Raamistikud ja sisuhaldussüsteemid toovad kaasa oma tööriistad. WordPressis täidab selle ülesande $wpdb->prepare(), Laravelis ja Symfonys päringuehitajad (Query Builder) ning ORM-id. Niipea kui kasutate seal toorest SQL-i, näiteks funktsioonidega, mille nimes on „raw“, kehtivad jälle selle lõigu reeglid. Laraveli vihjed leiate lehelt Laravel-rakenduste turvamine.
Otsige koodist sihipäraselt SQL-tekste, kuhu muutujaid sisse pannakse. Lihtne algus on otsing query( ja ->raw koos $_GET, $_POST või $_REQUEST kasutusega.
SQL-injektsiooni otsimine olemasolevas koodis
Uue tarkvara puhul saab reeglit algusest peale järgida. Keerulisem on rakendustega, mis on aastate jooksul kasvanud. Seal tasub süstemaatiliselt otsida kohti, kus SQL-tekstid pannakse muutujatega kokku:
# SQL-käsud, kuhu muutujaid sisse pannakse
grep -rnE "(SELECT|INSERT|UPDATE|DELETE)[^;]*\\\$[a-zA-Z_]" src/ --include="*.php" | head -50
# Päringuandmete otsene kasutus päringufunktsioonides
grep -rnE "(query|exec|raw)\s*\(.*\\\$_(GET|POST|REQUEST|COOKIE)" src/ --include="*.php"
Mitte iga tabamus ei ole turvaauk, näiteks kui muutuja sisaldab ainult kindlat tabelinime. Iga leiukoht tuleks siiski ühe korra teadlikult üle vaadata ja vajaduse korral Prepared Statementidele ümber seada. Staatilise koodianalüüsi tööriistad aitavad selliseid mustreid püsivalt ehituse torus (build pipeline) tuvastada, nii et uued vead tööle ei jõuagi.
Kahju piiramine: õigused, veateated, kontroll
Turvaline kood on esimene kaitseliin. Lisatasandid piiravad kahju, kui mõni auk siiski märkamata jääb:
- Minimaalsed õigused: Veebisaidi andmebaasikasutaja vajab enamasti ainult
SELECT,INSERT,UPDATEjaDELETEõigusi oma andmebaasile, mitte õigusi kasutajate loomiseks ega juurdepääsuks teistele andmebaasidele. - Veateateid väljapoole ei anta:
display_errors = Offphp.ini-s. Andmebaasivead kuuluvad logisse, mitte brauserisse. - Lahutus rakenduse kaupa: Iga veebisait saab oma andmebaasikasutaja.
- Sõltuvusi ajakohasena hoida: Turvauke on ka teekides, nagu näitab juhend composer audit.
- Kontrollida lasta: Koodi ülevaatus ja automatiseeritud analüüs leiavad palju kohti, penetratsioonitest näitab, mis on väljastpoolt tegelikult kättesaadav.
-- Näide: andmebaasikasutaja ainult vajalike õigustega
CREATE USER 'shop_app'@'localhost' IDENTIFIED BY '<starkes Passwort>';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'shop_app'@'localhost';
Sugulasnõrkus puudutab andmete väljastamist brauseris. Kuidas seda turvata, selgitab juhend Cross-Site-Scriptingu vältimine PHP-s.
Kuidas PHP SOS konkreetselt aitab?
- Kontrollime teie rakenduse andmebaasipäringuid süstemaatiliselt ebaturvaliste päringute suhtes.
- Prioriseerime leide kättesaadavuse ja mõjutatud andmete järgi ning näitame konkreetseid parandusi.
- Seame andmebaasiõigused ja veakäsitluse turvalisele tasemele.
- Testime pärast parandust uuesti ja dokumenteerime tulemuse.
Sõltumatut kontrolli kirjeldab teenus Turvaaudit ja penetratsioonitest.
Korduma kippuvad küsimused
Kas piisab sisendi töötlemisest addslashes või real_escape_string abil?
Ei, see on veaaldis. Põgenduse (escaping) toimimine sõltub märgistikust ja kontekstist ning see unustatakse kergesti ühes kohas. Prepared Statements lahutavad käsu ja andmed põhimõtteliselt ning on seetõttu soovitatud tee.
Kas ORM kaitseb SQL-injektsiooni eest automaatselt?
Suures osas, kuni kasutate ORM-i päringufunktsioone. Niipea kui kirjutate toorest SQL-i või paigutate väärtusi sorteerimistesse ja veerunimedesse, kehtivad samad reeglid nagu ilma ORM-ita.
Kas veebirakenduse tulemüür aitab SQL-injektsiooni vastu?
WAF võib teadaolevad mustrid kinni püüda ja aega võita. See ei asenda aga turvalist koodi, sest päringuandmete põhjal koostatud reegleid saab kõrvale kalduda ja need tabavad vahel legitiimset sisendit.
Kas PHP2Shelli vastane PHP uuendus sulgeb ka SQL-injektsiooni turvaauke?
Ei. PHP2Shell puudutab PHP interpretaatorit. SQL-injektsioon on rakenduse koodi viga ja jääb alles, olenemata sellest, milline PHP versioon töötab.