# Ingebouwde beveiligingsfuncties van PHP

> PHP-beveiligingsfuncties in een overzicht: wachtwoorden, toeval, escaping, cryptografie, sessies en waar hun grenzen liggen bij lekken zoals PHP2Shell.

Bron: https://phpsos.de/nl/php/eingebaute-sicherheitsfunktionen/  
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:** PHP levert voor de meest voorkomende beveiligingstaken kant-en-klare, geteste functies mee: `password_hash()` voor wachtwoorden, `random_bytes()` en `random_int()` voor tokens, `htmlspecialchars()` tegen cross-site scripting, voorbereide queries met PDO tegen SQL-injectie, `hash_equals()` voor veilige vergelijkingen en de Sodium-extensie voor cryptografie. Wie deze bouwstenen consequent gebruikt, vermijdt de meeste toepassingslekken. Tegen fouten in de PHP-interpreter zelf, zoals bij PHP2Shell, helpen ze niet; daar helpt alleen de update.

## Waarom ingebouwde PHP-beveiligingsfuncties?

Beveiligingscode zelf schrijven is foutgevoelig. Een eigen wachtwoordhash, een zelfgebouwde tokengenerator of een zelfgemaakt filter voor HTML-uitvoer zien er vaak goed uit en zijn het toch niet. De ingebouwde functies zijn gedocumenteerd, getest en worden met PHP-updates onderhouden.

Voor beslissers betekent dat: vraag uw bureau of ontwikkelteam niet of beveiligingsfuncties zelf zijn gebouwd, maar of de standaardfuncties van de taal worden gebruikt. De tabel geeft een overzicht.

| Taak | Functie | Beschikbaar sinds | Belangrijke aanwijzing |
|---|---|---|---|
| Wachtwoorden opslaan | `password_hash()`, `password_verify()` | PHP 5.5 | kolom tot 255 tekens; bcrypt kapt af op 72 bytes |
| Willekeurige tokens | `random_bytes()`, `random_int()` | PHP 7.0 | nooit `rand()` of `mt_rand()` voor geheimen |
| Toeval objectgeoriënteerd | `Random\Randomizer` | PHP 8.2 | dezelfde veiligheid, flexibelere interface |
| HTML-uitvoer escapen | `htmlspecialchars()` | van oudsher | standaardvlaggen sinds 8.1 escapen ook enkele aanhalingstekens |
| Vergelijking in constante tijd | `hash_equals()` | PHP 5.6 | gebruikersinvoer als tweede argument |
| Moderne cryptografie | Sodium (`sodium_*`) | PHP 7.2 | in plaats van eigen versleuteling |
| Geheimen in stacktraces | `#[\SensitiveParameter]` | PHP 8.2 | waarde verschijnt niet in de trace |
| Invoer controleren | `filter_var()` | PHP 5.2 | controleert formaat, vervangt geen escaping |

## Wachtwoorden en tokens

`password_hash()` maakt een gezouten hash, `password_verify()` controleert hem. Met `PASSWORD_DEFAULT` gebruikt PHP momenteel bcrypt; sinds PHP 8.4 ligt de standaardkostenfactor volgens de handleiding op 12 in plaats van 10. Omdat het standaardalgoritme kan veranderen, adviseert de handleiding een databasekolom van 255 tekens. Bij bcrypt worden wachtwoorden na 72 bytes afgekapt, wat bij zeer lange wachtzinnen in acht moet worden genomen.

```php
<?php
// Registratie
$hash = password_hash($passwort, PASSWORD_DEFAULT);

// Aanmelding, inclusief automatische opwaardering van oude hashes
if (password_verify($eingabe, $hash)) {
    if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
        $hash = password_hash($eingabe, PASSWORD_DEFAULT); // opnieuw opslaan
    }
}

// Token voor een link om het wachtwoord opnieuw in te stellen
$token = bin2hex(random_bytes(32));
```

Voor vergelijkingen van geheimen, bijvoorbeeld een handtekening, hoort `hash_equals()` te worden gebruikt. Een gewone vergelijking met `===` kan volgens de handleiding via de looptijd verraden vanaf welke plaats twee waarden verschillen. Uitgebreider: [Wachtwoorden in PHP veilig opslaan](https://phpsos.de/nl/blog/passwoerter-in-php-speichern/) en [veilige willekeurige getallen en tokens](https://phpsos.de/nl/blog/sichere-tokens-php/).

## Uitvoer escapen en de database veilig bevragen

Cross-site scripting ontstaat wanneer gebruikersinvoer ongefilterd in HTML terechtkomt. `htmlspecialchars()` zet speciale tekens om in HTML-entiteiten. Sinds PHP 8.1 zijn de standaardvlaggen `ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401`; daarvoor werden enkele aanhalingstekens niet omgezet. Wie oudere versies moet ondersteunen, geeft de vlaggen uitdrukkelijk op.

SQL-injectie voorkomen voorbereide queries. De waarden worden gescheiden van de SQL-tekst overgedragen en kunnen de query niet wijzigen:

```php
<?php
$stmt = $pdo->prepare('SELECT id, name FROM kunden WHERE email = :email');
$stmt->execute(['email' => $email]);
$kunde = $stmt->fetch();

echo htmlspecialchars($kunde['name'] ?? '', ENT_QUOTES, 'UTF-8');
```

De details staan onder [Cross-site scripting in PHP voorkomen](https://phpsos.de/nl/ratgeber/xss-php/) en op de pagina over SQL-injectie.

Invoer controleren is een tweede, afzonderlijke stap. `filter_var()` stelt vast of een waarde een geldig formaat heeft, bijvoorbeeld een e-mailadres of een geheel getal binnen een toegestaan bereik. Dat vervangt echter geen escaping bij de uitvoer en geen voorbereide query, want ook formeel geldige waarden kunnen speciale tekens bevatten.

## Geheimen uit foutmeldingen houden

Een onderschat datalek zijn stacktraces. Treedt in een functie een fout op, dan legt PHP de aanroepketen vast inclusief argumenten. Staat daarin een wachtwoord of API-sleutel, dan belandt die in het log. Sinds PHP 8.2 markeert het attribuut `#[\SensitiveParameter]` zulke parameters; hun waarde verschijnt in de trace dan alleen als object `SensitiveParameterValue`.

```php
<?php
function anmelden(string $benutzer, #[\SensitiveParameter] string $passwort): bool
{
    // ... controle ...
    throw new RuntimeException('Datenbank nicht erreichbar');
}
```

Volgens een aanwijzing in de handleiding werkt het attribuut alleen bij de concrete implementatie, niet bij de methode van een interface. Plaats het daarom daar waar de code daadwerkelijk draait.

## Sessies: veilige instellingen en nieuwe standaarden

Sessies zijn een veelvoorkomend aanvalsdoel. Drie instellingen zijn centraal: `session.use_strict_mode=1` weigert sessie-ID's die de server niet zelf heeft gemaakt; `session.cookie_httponly=1` verhindert toegang tot de sessiecookie via JavaScript; `session.cookie_samesite=Lax` bemoeilijkt het meesturen bij vreemde sites. Volgens de aangenomen RFC worden precies deze drie waarden met PHP 8.6 de standaard. Stel ze tot die tijd zelf in, en ook daarna uitdrukkelijk, zodat de configuratie niet van de standaard afhangt.

```ini
session.use_strict_mode = 1
session.cookie_httponly = 1
session.cookie_samesite = Lax
session.cookie_secure = 1
```

Meer daarover onder [Sessies in PHP beveiligen](https://phpsos.de/nl/ratgeber/session-sicherheit-php/).

## Wat ingebouwde functies niet kunnen

Het PHP-project is in zijn beveiligingsbeleid duidelijk: PHP is geen sandbox. Omzeilingen van `open_basedir` of `disable_functions`, kwaadaardige invoer voor `unserialize()` en ongecontroleerde argumenten voor functies als `mysqli_query()` gelden niet als beveiligingslekken van PHP. Voor beheerders betekent dat: deze instellingen zijn nuttige hardening, maar geen beveiligingsgrens, en `unserialize()` heeft met vreemde gegevens niets te maken.

Het tweede punt betreft PHP2Shell. CVE-2026-109618 en CVE-2026-109619 maken volgens php.net code-uitvoering mogelijk voordat of terwijl uw toepassing draait; bij mod_php is een exploit getoond voordat er überhaupt toepassingscode wordt uitgevoerd. Geen enkele functie in uw code kan dat opvangen. Veilig programmeren en een actuele PHP-versie zijn twee afzonderlijke plichten, en beide moeten worden vervuld.

## Hoe helpt PHP SOS concreet?

In de [beveiligingsaudit voor PHP-toepassingen](https://phpsos.de/nl/leistungen/sicherheitsaudit-penetrationstest/) controleren wij gericht:

- **Wachtwoord- en tokenafhandeling:** Worden `password_hash()` en `random_bytes()` gebruikt, of zijn er eigen constructies?
- **Uitvoer en queries:** Waar komt invoer ongefilterd in HTML, SQL of shell-opdrachten terecht?
- **Sessieconfiguratie:** Zijn strict mode, HttpOnly, SameSite en Secure ingesteld en niet te overschrijven?
- **Looptijd:** Draait er een ondersteunde PHP-versie met de correcties van 21 oktober 2026?


## Veelgestelde vragen

**Hoe slaat men wachtwoorden in PHP goed op?**  
Met password_hash() en PASSWORD_DEFAULT; gecontroleerd wordt met password_verify(). De databasekolom moet volgens de handleiding tot 255 tekens kunnen bevatten, omdat het standaardalgoritme in de toekomst kan veranderen.

**Is rand() geschikt voor tokens?**  
Nee. Voor tokens, sleutels en wachtwoordlinks horen random_bytes() of random_int() te worden gebruikt. Ze leveren volgens de handleiding cryptografisch veilige willekeurige waarden.

**Sinds wanneer zit Sodium in PHP?**  
De Sodium-extensie voor moderne cryptografie maakt sinds PHP 7.2.0 deel uit van PHP. Oudere versies konden haar alleen via PECL achteraf toevoegen.

**Beschermen deze functies tegen PHP2Shell?**  
Nee. Ze beschermen tegen fouten in uw toepassing. PHP2Shell zit in de interpreter zelf en wordt alleen gedicht met een PHP-update, gevolgd door de herstart van alle PHP-processen.

**Veranderen de sessiestandaarden in PHP 8.6?**  
Ja, volgens de aangenomen RFC worden session.use_strict_mode en session.cookie_httponly standaard ingeschakeld en wordt session.cookie_samesite op Lax gezet. Bestaande configuraties moet u toch zelf uitdrukkelijk instellen.

## Bronnen

- [PHP-handleiding: password_hash()](https://www.php.net/manual/en/function.password-hash.php)
- [PHP-handleiding: random_bytes()](https://www.php.net/manual/en/function.random-bytes.php)
- [PHP-handleiding: hash_equals()](https://www.php.net/manual/en/function.hash-equals.php)
- [PHP RFC: Secure Session Configuration Defaults](https://wiki.php.net/rfc/session_security_defaults)
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
