# Preprečevanje SQL injection v PHP

> Preprečevanje SQL injection v PHP: prepared statements s PDO in mysqli, seznami dovoljenih stolpcev, ozke pravice zbirke. S primeri kode.

Vir: https://phpsos.de/sl/ratgeber/sql-injection-php/  
Stanje: 25.10.2026  
Ponudnik: PHP SOS, Nujna pomoč za PHP in varnost strežnikov. Takojšnja pomoč po WhatsAppu: https://wa.me/491736494914?text=Pozdravljeni%2C%20PHP%20SOS%2C%20potrebujemo%20pomo%C4%8D%20z%20na%C5%A1im%20stre%C5%BEnikom%20PHP.%20Podjetje%3A%20%E2%80%A6%20Spletna%20stran%3A%20%E2%80%A6

**Na kratko:** SQL injection v PHP zanesljivo preprečite tako, da vrednosti nikoli ne vgrajujete v besedila SQL, ampak jih vedno predajate prek prepared statements z nadomestnimi znaki. Za imena stolpcev in tabel, ki jih ni mogoče predati kot nadomestne znake, velja trden seznam dovoljenih vrednosti. Poleg tega uporabnik podatkovne zbirke z minimalnimi pravicami omeji škodo, če vendarle ostane ranljivost.

## Zakaj SQL injection po PHP2Shell ne sme izginiti iz vidnega polja

Oktobra 2026 se veliko vrti okoli PHP2Shell, dveh kritičnih ranljivosti v interpreterju PHP, odpravljenih od 21. 10. 2026 v PHP 8.5.12, 8.4.27, 8.3.36 in 8.2.35. Številne ekipe so te dni posodobile strežnike in znova zagnale procese. To je prav in nujno.

Aktualno stanje PHP pa o lastni kodi ne pove ničesar. SQL injection je že leta ena najpogostejših šibkosti spletnih aplikacij in je v OWASP Top 10 uvrščen v kategorijo Injection. Kdor tako ali tako načrtuje varnostna preverjanja, naj hkrati pogleda dostope aplikacije do podatkovne zbirke.

Dobra novica: SQL injection je ena redkih šibkosti, ki jo je mogoče z enostavnim, dosledno uporabljenim pravilom skoraj v celoti izključiti.

## Kako nastane SQL injection v PHP?

SQL injection nastane, ko aplikacija uporabniške vnose obravnava kot del ukaza SQL namesto kot čiste podatke. Značilno je sestavljanje poizvedbe iz besedilnih gradnikov in spremenljivk. Če lahko obiskovalec določi vsebino take spremenljivke, lahko pod določenimi pogoji spremeni pomen ukaza.

Posledice segajo od branja tujih podatkov strank prek spreminjanja cen ali pravic do brisanja celotnih tabel. Glede na podatkovno zbirko in pravice lahko ranljivost omogoči tudi dostope onkraj podatkovne zbirke. Če so prizadeti osebni podatki, lahko nastane obveznost prijave po GDPR.

Vnosi niso samo polja obrazcev. Tudi parametri URL, piškotki, glave HTTP, naložene datoteke, odgovori tujih vmesnikov in že shranjeni podatki iz lastne podatkovne zbirke veljajo za nezaupanja vredne.

## Prepared statements s PDO in mysqli

Prepared statements ukaz SQL in vrednosti pošljejo podatkovni zbirki ločeno. Vrednosti se nikoli ne razlagajo kot del ukaza. S PDO je varna poizvedba videti tako:

```php
<?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();
```

Možnost `PDO::ATTR_EMULATE_PREPARES => false` poskrbi, da podatkovna zbirka uporablja prave prepared statements, kolikor gonilnik to podpira. Z mysqli velja isto načelo:

```php
<?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();
```

Tudi sezname za `IN (...)` je mogoče varno izvesti tako, da za vsako vrednost ustvarite lasten nadomestni znak in vrednosti predate ločeno.

```php
<?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);
```

## Posebni primeri: imena stolpcev, razvrščanje in surov SQL

Nadomestni znaki delujejo samo za vrednosti, ne za imena tabel, imena stolpcev ali smeri razvrščanja. Prav tu v praksi nastane veliko ranljivosti, na primer pri razvrstljivih tabelah ali filtrih. Rešitev je trden seznam dovoljenih vrednosti:

```php
<?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");
```

Ogrodja in CMS-ji prinesejo lastna orodja. V WordPressu to nalogo prevzame `$wpdb->prepare()`, v Laravelu in Symfonyju pa query builderji in ORM-ji. Takoj ko tam uporabite surov SQL, na primer prek funkcij z „raw“ v imenu, znova veljajo pravila tega razdelka. Napotke za Laravel najdete na strani [Zaščita aplikacij Laravel](https://phpsos.de/sl/plattformen/laravel/).

> **Nasvet:**
> V kodi ciljno iščite besedila SQL, v katera se vstavljajo spremenljivke. Preprost začetek je iskanje `query(` in `->raw` v kombinaciji z `$_GET`, `$_POST` ali `$_REQUEST`.

## Iskanje SQL injection v obstoječi kodi

Pri novi programski opremi je mogoče pravilo upoštevati od začetka. Težje je pri aplikacijah, ki so rasle leta. Tam se splača sistematično iskati mesta, kjer se besedila SQL sestavljajo s spremenljivkami:

```bash
# Ukazi SQL, v katere se vstavljajo spremenljivke
grep -rnE "(SELECT|INSERT|UPDATE|DELETE)[^;]*\\\$[a-zA-Z_]" src/ --include="*.php" | head -50
# Neposredna uporaba podatkov zahteve v poizvedovalnih funkcijah
grep -rnE "(query|exec|raw)\s*\(.*\\\$_(GET|POST|REQUEST|COOKIE)" src/ --include="*.php"
```

Vsak zadetek ni ranljivost, na primer kadar spremenljivka vsebuje le fiksno ime tabele. Vsako najdeno mesto pa je treba enkrat zavestno preveriti in po potrebi preurediti na prepared statements. Orodja za statično analizo kode pomagajo takšne vzorce trajno prepoznavati v cevovodu gradnje, tako da nove napake sploh ne pridejo v živo.

## Omejevanje škode: pravice, sporočila o napakah, preverjanje

Varna koda je prva obrambna linija. Nadaljnje ravni omejijo škodo, če se ranljivost vendarle spregleda:

- **Minimalne pravice:** Uporabnik podatkovne zbirke spletne strani običajno potrebuje samo `SELECT`, `INSERT`, `UPDATE` in `DELETE` na lastni podatkovni zbirki, ne pravic za ustvarjanje uporabnikov ali dostop do drugih podatkovnih zbirk.
- **Brez sporočil o napakah navzven:** `display_errors = Off` v php.ini. Napake podatkovne zbirke sodijo v dnevnik, ne v brskalnik.
- **Ločitev po aplikacijah:** Vsaka spletna stran dobi lastnega uporabnika podatkovne zbirke.
- **Odvisnosti ohranjajte aktualne:** Ranljivosti so tudi v knjižnicah, kot kaže vodnik [composer audit](https://phpsos.de/sl/ratgeber/composer-abhaengigkeiten-pruefen/).
- **Dajte preveriti:** Pregled kode in samodejna analiza najdeta veliko mest, penetracijski test pa pokaže, kaj je od zunaj dejansko dosegljivo.

```sql
-- Primer: uporabnik podatkovne zbirke samo z nujnimi pravicami
CREATE USER 'shop_app'@'localhost' IDENTIFIED BY '<starkes Passwort>';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'shop_app'@'localhost';
```

Sorodna ranljivost zadeva izpis podatkov v brskalniku. Kako jo zaščitite, pojasnjuje vodnik [Preprečevanje Cross-Site Scripting v PHP](https://phpsos.de/sl/ratgeber/xss-php/).

## Kako PHP SOS pomaga konkretno?

- Dostope vaše aplikacije do podatkovne zbirke sistematično preverimo glede nevarnih poizvedb.
- Najdbe razvrstimo po dosegljivosti in prizadetih podatkih ter pokažemo konkretne popravke.
- Pravice podatkovne zbirke in obravnavo napak postavimo na varno raven.
- Po popravku ponovno preizkusimo in rezultat dokumentiramo.

Neodvisno preverjanje opisuje storitev [Varnostni pregled in penetracijski test](https://phpsos.de/sl/leistungen/sicherheitsaudit-penetrationstest/).


## Pogosta vprašanja

**Ali zadošča, da vnose obdelam z addslashes ali real_escape_string?**  
Ne, to je dovzetno za napake. Escaping je odvisen od nabora znakov in konteksta, na enem mestu ga je lahko pozabiti. Prepared statements ukaz in podatke načelno ločijo in so zato priporočena pot.

**Ali ORM samodejno ščiti pred SQL injection?**  
V veliki meri, dokler uporabljate poizvedovalne funkcije ORM. Takoj ko pišete surov SQL ali vrednosti vstavljate v razvrščanje in imena stolpcev, veljajo ista pravila kot brez ORM.

**Ali spletni požarni zid (WAF) pomaga proti SQL injection?**  
WAF lahko prestreže znane vzorce in pridobi čas. Varne kode pa ne nadomesti, saj je pravila na podlagi podatkov zahteve mogoče zaobiti in včasih zadenejo tudi zakonite vnose.

**Ali posodobitev PHP proti PHP2Shell zapre tudi ranljivosti SQL injection?**  
Ne. PHP2Shell zadeva interpreter PHP. SQL injection je napaka v kodi aplikacije in ostane, ne glede na to, katera različica PHP teče.

## Viri

- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
- [php.net: varnostna obvestila](https://www.php.net/)
