# SQL-injectie in PHP voorkomen

> SQL-injectie in PHP voorkomen: prepared statements met PDO en mysqli, whitelists voor kolommen en beperkte databaserechten, met codevoorbeelden.

Bron: https://phpsos.de/nl/ratgeber/sql-injection-php/  
Stand: 25-10-2026  
Aanbieder: PHP SOS, PHP-noodhulp en serverbeveiliging. Directe hulp via WhatsApp: https://wa.me/491736494914?text=Hallo%20PHP%20SOS%2C%20wij%20hebben%20hulp%20nodig%20met%20onze%20PHP-server.%20Bedrijf%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**Kort gezegd:** SQL-injectie in PHP voorkomt u betrouwbaar door waarden nooit in SQL-teksten te verwerken, maar altijd via prepared statements met plaatshouders door te geven. Voor kolom- en tabelnamen, die niet als plaatshouder kunnen worden doorgegeven, geldt een vaste whitelist. Daarnaast beperkt een databasegebruiker met minimale rechten de schade als er toch een kwetsbaarheid overblijft.

## Waarom SQL-injectie na PHP2Shell niet uit beeld mag raken

In oktober 2026 draait veel om PHP2Shell, twee kritieke kwetsbaarheden in de PHP-interpreter, sinds 21-10-2026 verholpen in PHP 8.5.12, 8.4.27, 8.3.36 en 8.2.35. Veel teams hebben in die dagen servers bijgewerkt en processen herstart. Dat is terecht en dringend.

Een actuele PHP-stand zegt echter niets over de eigen code. SQL-injectie hoort al jaren bij de meest voorkomende zwakke plekken in webtoepassingen en staat in de OWASP Top 10 onder de categorie injection. Wie toch al beveiligingscontroles plant, moet de databasetoegang van de eigen toepassing meteen meenemen.

Het goede nieuws: SQL-injectie is een van de weinige zwakke plekken die met één eenvoudige, consequent toegepaste regel bijna volledig uit te sluiten is.

## Hoe ontstaat SQL-injectie in PHP?

SQL-injectie ontstaat wanneer een toepassing gebruikersinvoer behandelt als onderdeel van een SQL-opdracht in plaats van als pure gegevens. Typisch is het samenstellen van een query uit tekstbouwstenen en variabelen. Kan een bezoeker de inhoud van zo'n variabele bepalen, dan kan hij onder omstandigheden de betekenis van de opdracht veranderen.

De gevolgen variëren van het uitlezen van vreemde klantgegevens via het wijzigen van prijzen of rechten tot het wissen van hele tabellen. Afhankelijk van database en rechten kan een kwetsbaarheid ook toegang buiten de database mogelijk maken. Zijn persoonsgegevens getroffen, dan kan een meldplicht volgens de AVG ontstaan.

Invoer is daarbij niet alleen een formulierveld. Ook URL-parameters, cookies, HTTP-headers, geüploade bestanden, antwoorden van externe koppelingen en reeds opgeslagen gegevens uit de eigen database gelden als niet betrouwbaar.

## Prepared statements met PDO en mysqli

Prepared statements sturen de SQL-opdracht en de waarden gescheiden naar de database. De waarden worden nooit als deel van de opdracht geïnterpreteerd. Met PDO ziet een veilige query er zo uit:

```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();
```

De optie `PDO::ATTR_EMULATE_PREPARES => false` zorgt ervoor dat de database echte prepared statements gebruikt, voor zover de driver dat ondersteunt. Met mysqli geldt hetzelfde principe:

```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();
```

Ook lijsten voor `IN (...)` zijn veilig weer te geven, door voor elke waarde een eigen plaatshouder te genereren en de waarden gescheiden door te geven.

```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);
```

## Bijzondere gevallen: kolomnamen, sortering en ruwe SQL

Plaatshouders werken alleen voor waarden, niet voor tabelnamen, kolomnamen of sorteerrichtingen. Juist hier ontstaan in de praktijk veel kwetsbaarheden, bijvoorbeeld bij sorteerbare tabellen of filters. De oplossing is een vaste lijst van toegestane waarden:

```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");
```

Frameworks en CMS'en leveren eigen hulpmiddelen mee. In WordPress neemt `$wpdb->prepare()` die taak over, in Laravel en Symfony de query builders en ORM's. Zodra u daar ruwe SQL gebruikt, bijvoorbeeld via functies met „raw” in de naam, gelden weer de regels van deze paragraaf. Aanwijzingen over Laravel vindt u onder [Laravel-toepassingen beveiligen](https://phpsos.de/nl/plattformen/laravel/).

> **Tip:**
> Zoek in de code gericht naar SQL-teksten waarin variabelen worden ingevoegd. Een eenvoudige start is een zoekopdracht naar `query(` en `->raw` in combinatie met `$_GET`, `$_POST` of `$_REQUEST`.

## SQL-injectie in bestaande code opsporen

Bij nieuwe software is de regel van begin af aan vol te houden. Moeilijker is het bij toepassingen die over jaren zijn gegroeid. Daar loont een systematische zoektocht naar plekken waar SQL-teksten met variabelen worden samengesteld:

```bash
# SQL-opdrachten waarin variabelen worden ingevoegd
grep -rnE "(SELECT|INSERT|UPDATE|DELETE)[^;]*\\\$[a-zA-Z_]" src/ --include="*.php" | head -50
# Rechtstreeks gebruik van aanvraaggegevens in queryfuncties
grep -rnE "(query|exec|raw)\s*\(.*\\\$_(GET|POST|REQUEST|COOKIE)" src/ --include="*.php"
```

Niet elke treffer is een kwetsbaarheid, bijvoorbeeld als de variabele alleen een vaste tabelnaam bevat. Elke vindplaats moet echter één keer bewust worden gecontroleerd en zo nodig op prepared statements worden omgezet. Hulpmiddelen voor statische codeanalyse helpen zulke patronen blijvend in de buildpipeline te herkennen, zodat nieuwe fouten helemaal niet live gaan.

## Schade beperken: rechten, foutmeldingen, controle

Veilige code is de eerste verdedigingslinie. Verdere lagen beperken de schade als er toch een kwetsbaarheid over het hoofd wordt gezien:

- **Minimale rechten:** De databasegebruiker van de website heeft meestal alleen `SELECT`, `INSERT`, `UPDATE` en `DELETE` op de eigen database nodig, geen rechten om gebruikers aan te maken of andere databases te benaderen.
- **Geen foutmeldingen naar buiten:** `display_errors = Off` in de php.ini. Databasefouten horen in het log, niet in de browser.
- **Scheiding per toepassing:** Elke website krijgt een eigen databasegebruiker.
- **Afhankelijkheden actueel houden:** Kwetsbaarheden zitten ook in bibliotheken, zoals de gids [composer audit](https://phpsos.de/nl/ratgeber/composer-abhaengigkeiten-pruefen/) laat zien.
- **Laten controleren:** Code review en geautomatiseerde analyse vinden veel plekken, een penetratietest toont wat van buitenaf werkelijk bereikbaar is.

```sql
-- Voorbeeld: databasegebruiker met alleen de noodzakelijke rechten
CREATE USER 'shop_app'@'localhost' IDENTIFIED BY '<starkes Passwort>';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'shop_app'@'localhost';
```

Een verwante zwakke plek betreft de weergave van gegevens in de browser. Hoe u die beveiligt, legt de gids [Cross-site scripting in PHP voorkomen](https://phpsos.de/nl/ratgeber/xss-php/) uit.

## Hoe helpt PHP SOS concreet?

- We controleren de databasetoegang van uw toepassing systematisch op onveilige queries.
- We prioriteren vondsten naar bereikbaarheid en getroffen gegevens en tonen concrete correcties.
- We brengen databaserechten en foutafhandeling op een veilig niveau.
- We testen na de correctie opnieuw en documenteren het resultaat.

Een onafhankelijke controle beschrijft de dienst [Beveiligingsaudit en penetratietest](https://phpsos.de/nl/leistungen/sicherheitsaudit-penetrationstest/).


## Veelgestelde vragen

**Volstaat het om invoer te behandelen met addslashes of real_escape_string?**  
Nee, dat is foutgevoelig. Escapen hangt af van de tekenset en de context en wordt gemakkelijk op één plek vergeten. Prepared statements scheiden opdracht en gegevens principieel en zijn daarom de aanbevolen weg.

**Beschermt een ORM automatisch tegen SQL-injectie?**  
Grotendeels, zolang u de querymethoden van de ORM gebruikt. Zodra u ruwe SQL schrijft of waarden in sorteringen en kolomnamen opneemt, gelden dezelfde regels als zonder ORM.

**Helpt een Web Application Firewall tegen SQL-injectie?**  
Een WAF kan bekende patronen opvangen en tijd winnen. Ze vervangt echter geen veilige code, want regels op basis van aanvraaggegevens zijn te omzeilen en raken soms legitieme invoer.

**Dicht de PHP-update tegen PHP2Shell ook SQL-injectiekwetsbaarheden?**  
Nee. PHP2Shell betreft de PHP-interpreter. SQL-injectie is een fout in de toepassingscode en blijft bestaan, welke PHP-versie er ook draait.

## Bronnen

- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
- [php.net: beveiligingsmeldingen](https://www.php.net/)
