# Server-side request forgery (SSRF) in PHP voorkomen

> SSRF in PHP voorkomen: toegestane-doelenlijsten, alleen HTTPS, geen doorverwijzingen, omgezette IP controleren en uitgaand verkeer beperken. Met cURL-code.

Bron: https://phpsos.de/nl/blog/ssrf-in-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:** SSRF in PHP voorkomt u door uw applicatie alleen adressen te laten ophalen die op een vaste lijst met toegestane doelen staan, uitsluitend via HTTPS en zonder doorverwijzingen te volgen. Het omgezette IP-adres wordt gecontroleerd tegen privé- en gereserveerde bereiken en daarna voor de verbinding vastgehouden. Een firewall voor uitgaand verkeer zorgt ervoor dat interne systemen ook bij een fout in de code onbereikbaar blijven.

## Wanneer de eigen server verzoeken doet voor vreemden

Linkvoorbeelden in een chat, import van een productafbeelding via een URL, webhooks naar partnersystemen, een feedlezer in het CMS: veel PHP-applicaties halen adressen op die een gebruiker heeft opgegeven. Dat is een nuttige functie en tegelijk een deur naar binnen.

Bij server-side request forgery, kortweg SSRF, brengt een aanvaller de applicatie ertoe een verzoek te sturen naar een doel van zijn keuze. De clou: het verzoek komt van de server zelf. Daarmee bereikt het systemen die van buitenaf zijn afgesloten, zoals databases, beheerinterfaces in het interne netwerk, diensten die alleen op localhost luisteren of de metadatadiensten van cloudaanbieders.

In de OWASP Top 10 van 2021 was SSRF een eigen categorie. In PHP-projecten duikt de kwetsbaarheid vaak op waar iemand snel een functie "afbeelding via URL toevoegen" heeft gebouwd.

## SSRF in PHP: welke functies adressen ophalen

Niet alleen cURL haalt adressen op. Houd de volgende plekken in het oog:

| Functie of onderdeel | Waarom relevant |
|---|---|
| cURL (`curl_exec()`) | meest gebruikte weg, veel protocollen mogelijk |
| `file_get_contents()`, `fopen()`, `copy()` met URL | gebruiken URL-wrappers als `allow_url_fopen` actief is |
| `getimagesize()` en beeldbibliotheken | kunnen externe bestanden laden |
| XML-verwerking, SOAP-clients | laden onder omstandigheden externe bronnen |
| HTTP-bibliotheken zoals Guzzle | bouwen voort op cURL of streams, dezelfde regels |

Hoeft uw applicatie geen externe bestanden via bestandsfuncties te openen, schakel dan `allow_url_fopen` aan de serverkant uit. Een verwant onderwerp, het insluiten van bestanden via paden uit het verzoek, behandelt de gids [Local en remote file inclusion voorkomen](https://phpsos.de/nl/ratgeber/lfi-rfi/).

## De verdediging in vijf stappen

OWASP adviseert een combinatie van maatregelen die afzonderlijk telkens te omzeilen zouden zijn, maar samen standhouden:

1. **Lijst met toegestane doelen in plaats van vrije URL:** Het best kiest de gebruiker alleen uit bekende doelen, of hij geeft slechts een identificatie op en de applicatie bouwt het adres zelf.
2. **Alleen HTTPS toestaan:** Andere protocollen zoals `file`, `gopher` of `ftp` in cURL uitsluiten, ook voor doorverwijzingen.
3. **Geen doorverwijzingen volgen:** Een toegestaan adres kan anders doorverwijzen naar een intern doel.
4. **Omgezet adres controleren en vasthouden:** De hostnaam omzetten, het IP controleren tegen privé-, loopback- en link-local-bereiken en precies dit IP voor de verbinding gebruiken. Anders kan een tweede DNS-omzetting een ander doel opleveren.
5. **Netwerk beperken:** De webserver mag naar buiten alleen de doelen bereiken die hij nodig heeft.

## Een veilig patroon met cURL

Het volgende voorbeeld voert de stappen 1 tot en met 4 door voor IPv4. Hostnamen en verloop zijn voorbeelden; voor IPv6 gelden dezelfde controles.

```php
<?php
declare(strict_types=1);

function ladeVonPartner(string $url): string
{
    $teile = parse_url($url);
    if (!is_array($teile) || ($teile['scheme'] ?? '') !== 'https'
        || isset($teile['user']) || isset($teile['pass']) || isset($teile['port'])) {
        throw new InvalidArgumentException('Adresse nicht erlaubt.');
    }

    $host = strtolower($teile['host'] ?? '');
    $erlaubt = ['api.partner.example', 'bilder.partner.example'];
    if (!in_array($host, $erlaubt, true)) {
        throw new InvalidArgumentException('Ziel nicht erlaubt.');
    }

    $ip = gethostbyname($host);
    $oeffentlich = filter_var($ip, FILTER_VALIDATE_IP,
        FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
    if ($oeffentlich === false) {
        throw new RuntimeException('Ziel löst auf eine nicht erlaubte Adresse auf.');
    }

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER  => true,
        CURLOPT_FOLLOWLOCATION  => false,
        CURLOPT_PROTOCOLS       => CURLPROTO_HTTPS,
        CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTPS,
        CURLOPT_RESOLVE         => [$host . ':443:' . $ip],
        CURLOPT_CONNECTTIMEOUT  => 3,
        CURLOPT_TIMEOUT         => 10,
    ]);
    $antwort = curl_exec($ch);
    if ($antwort === false) {
        throw new RuntimeException('Abruf fehlgeschlagen.');
    }
    return $antwort;
}
```

Enkele details zijn belangrijk. `gethostbyname()` geeft bij een fout de hostnaam terug; de aansluitende IP-controle vangt dat op. De vlaggen `FILTER_FLAG_NO_PRIV_RANGE` en `FILTER_FLAG_NO_RES_RANGE` sluiten volgens de PHP-handleiding onder meer de bereiken 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8 en 169.254.0.0/16 uit. Sinds PHP 8.2 bestaat bovendien `FILTER_FLAG_GLOBAL_RANGE`, dat alleen globaal geldige adressen toestaat. `CURLOPT_RESOLVE` zorgt ervoor dat cURL precies het gecontroleerde adres gebruikt, terwijl hostnaam en certificaatcontrole behouden blijven.

Twee andere punten beperken wat een aanvaller uit een SSRF-kwetsbaarheid kan leren. Geef antwoorden en foutmeldingen van het opgehaalde doel niet ongefilterd terug aan de gebruiker: al het onderscheid tussen "verbinding geweigerd" en "time-out" verraadt welke interne diensten bestaan. En beperk looptijd en grootte van het antwoord, zodat de functie niet geschikt is om de eigen server te overbelasten. Webhooks naar adressen die klanten zelf invoeren, verstuurt u het best vanuit een wachtrij en niet tijdens het lopende verzoek.

> **Belangrijk:**
> Moet uw applicatie werkelijk willekeurige adressen ophalen, bijvoorbeeld voor linkvoorbeelden, besteed die functie dan uit aan een afzonderlijke dienst zonder toegang tot het interne netwerk. Een lijst met toegestane doelen is dan niet mogelijk, des te belangrijker is de netwerkscheiding.

## Het netwerkniveau als vangnet

Codecontroles kunnen lekken hebben, bijvoorbeeld wanneer twee bibliotheken een URL verschillend ontleden. OWASP adviseert zulke tegenstrijdigheden als afwijzing te behandelen en bovendien op netwerkniveau te beperken. Een webserver heeft naar buiten meestal maar enkele doelen nodig: pakketbronnen, betaal- en verzenddiensten, mailrelay, bepaalde interfaces.

Een firewall voor uitgaand verkeer helpt dubbel. Ze stopt SSRF-verzoeken naar het interne netwerk en ze bemoeilijkt na een inbraak, bijvoorbeeld via een niet-gepatchte PHP-versie, het nalaten van verdere hulpmiddelen. Hoe u de regels opbouwt, beschrijft [Firewall voor webservers: alleen noodzakelijke poorten openen](https://phpsos.de/nl/blog/firewall-fuer-webserver/). Welke verbindingen een server werkelijk opbouwt, laat [Uitgaande verbindingen van een server controleren](https://phpsos.de/nl/blog/ausgehende-verbindungen-pruefen/) zien.

In cloudomgevingen hoort de geharde variant van de metadatadienst erbij. OWASP noemt voor AWS de overstap naar IMDSv2 en het uitschakelen van IMDSv1.

## SSRF in bestaande code opsporen

Een snelle start is de zoektocht naar plekken waar ophaalfuncties waarden uit het verzoek krijgen:

```bash
grep -rnE "(curl_init|file_get_contents|fopen|getimagesize|copy)\s*\(" src/ --include="*.php" | grep -E '\$_(GET|POST|REQUEST)|\$url'
grep -rn "CURLOPT_FOLLOWLOCATION" src/ --include="*.php"
```

Treffers zijn controleopdrachten. Bekijk functies die beheerders gebruiken bijzonder grondig: imports, webhookinstellingen, updatecontroles van plug-ins.

Gemakkelijk over het hoofd gezien worden pdf-generatoren die HTML met ingesloten afbeeldingen of stylesheets omzetten in documenten. Komt een deel van die HTML uit gebruikersinvoer, bijvoorbeeld bij facturen met een vrij te kiezen logo, dan haalt de generator onder omstandigheden adressen op die de gebruiker bepaalt. Controleer daar de instellingen voor het laden van externe bronnen en schakel dat uit als het niet nodig is.

## Hoe helpt PHP SOS concreet?

- Wij vinden alle plekken waar uw applicatie adressen ophaalt, ook in plug-ins en bibliotheken.
- Wij bouwen een centrale ophaalfunctie met lijst van toegestane doelen, protocolbeperking en IP-controle.
- Wij stellen regels voor uitgaand verkeer vast en beschermen metadatadiensten in cloudomgevingen.
- Wij controleren de uitvoering van buitenaf en documenteren toegestane doelen.

Een onafhankelijke controle van uw applicatie beschrijft [beveiligingsaudit en penetratietest](https://phpsos.de/nl/leistungen/sicherheitsaudit-penetrationstest/).


## Veelgestelde vragen

**Welke PHP-functies kunnen voor SSRF worden misbruikt?**  
Alle functies die een adres uit gebruikersinvoer ophalen: cURL, file_get_contents en fopen met URL-wrappers, beeld- en XML-functies die externe bestanden laden, en bibliotheken voor webhooks, feeds of linkvoorbeelden.

**Is het genoeg om interne IP-adressen te blokkeren?**  
Alleen niet. OWASP noemt blokkadelijsten gemakkelijk te omzeilen en adviseert lijsten met toegestane doelen. Bovendien moet de applicatie precies het adres gebruiken dat ze heeft gecontroleerd en mag ze geen doorverwijzingen volgen.

**Waarom is SSRF in de cloud bijzonder kritiek?**  
Cloudaanbieders stellen aan instanties interne metadatadiensten ter beschikking, waarmee deels toegangsgegevens zijn op te halen. Kan een applicatie daarheen verzoeken sturen, dan kan een aanvaller die gegevens via haar bereiken. De aanbieders bieden geharde varianten van deze diensten aan, die geactiveerd moeten zijn.

**Wat levert een firewall voor uitgaand verkeer op?**  
Ze beperkt welke doelen de server überhaupt kan bereiken. Zelfs als een controle in de code faalt, blijven interne systemen dan onbereikbaar. Bovendien bemoeilijkt ze na een inbraak het nalaten van schadelijke code en het weglekken van gegevens.

## Bronnen

- [OWASP Server-Side Request Forgery Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)
- [PHP-handleiding: filterconstanten](https://www.php.net/manual/de/filter.constants.php)
- [PHP-handleiding: cURL-constanten](https://www.php.net/manual/de/curl.constants.php)
