# Prevence Server-Side Request Forgery (SSRF) v PHP

> SSRF v PHP: seznamy povolených cílů, jen HTTPS, žádná přesměrování, kontrola přeložené IP a omezení odchozího provozu. S ukázkou kódu cURL.

Zdroj: https://phpsos.de/cs/blog/ssrf-in-php/  
Stav: 25.10.2026  
Poskytovatel: PHP SOS, Nouzová pomoc pro PHP a bezpečnost serverů. Okamžitá pomoc přes WhatsApp: https://wa.me/491736494914?text=Dobr%C3%BD%20den%2C%20PHP%20SOS%2C%20pot%C5%99ebujeme%20pomoc%20s%20na%C5%A1%C3%ADm%20PHP%20serverem.%20Firma%3A%20%E2%80%A6%20Web%3A%20%E2%80%A6

**Stručně:** SSRF v PHP zabráníte tak, že vaše aplikace stahuje jen adresy z pevného seznamu povolených cílů, výhradně přes HTTPS a bez následování přesměrování. Přeložená adresa IP se zkontroluje proti privátním a rezervovaným rozsahům a poté se pro spojení zafixuje. Firewall pro odchozí provoz zajistí, že zůstanou interní systémy nedostupné i při chybě v kódu.

## Když vlastní server posílá požadavky za cizí osoby

Náhledy odkazů v chatu, import obrázku produktu přes URL, webhooky do partnerských systémů, čtečka kanálů v CMS: mnoho aplikací PHP stahuje adresy, které zadal uživatel. Je to užitečná funkce a zároveň dveře dovnitř.

U Server-Side Request Forgery, zkráceně SSRF, přiměje útočník aplikaci odeslat požadavek na cíl dle své volby. Háček: požadavek přichází ze samotného serveru. Dosáhne tak systémů, které jsou zvenčí uzavřené, například databází, administračních rozhraní ve vnitřní síti, služeb, které naslouchají jen na localhost, nebo služeb metadat poskytovatelů cloudu.

V OWASP Top 10 z roku 2021 bylo SSRF samostatnou kategorií. V projektech PHP se zranitelnost často objevuje tam, kde někdo rychle postavil funkci „přidat obrázek přes URL“.

## SSRF v PHP: které funkce stahují adresy

Adresy nestahuje jen cURL. Na tato místa byste si měli dávat pozor:

| Funkce nebo oblast | Proč je relevantní |
|---|---|
| cURL (`curl_exec()`) | nejčastější cesta, možné mnoho protokolů |
| `file_get_contents()`, `fopen()`, `copy()` s URL | používají obálky URL, pokud je aktivní `allow_url_fopen` |
| `getimagesize()` a knihovny pro obrázky | mohou načítat vzdálené soubory |
| zpracování XML, klienti SOAP | za určitých okolností načítají externí zdroje |
| knihovny HTTP jako Guzzle | stavějí na cURL nebo streamech, stejná pravidla |

Pokud vaše aplikace nepotřebuje otevírat vzdálené soubory přes souborové funkce, vypněte `allow_url_fopen` na straně serveru. Příbuzné téma, vkládání souborů přes cesty z požadavku, řeší příručka [Prevence Local a Remote File Inclusion](https://phpsos.de/cs/ratgeber/lfi-rfi/).

## Obrana v pěti krocích

OWASP doporučuje kombinaci opatření, která by šla jednotlivě obejít, ale společně drží:

1. **Seznam povolených hodnot místo volné URL:** Nejlépe uživatel vybírá jen ze známých cílů, nebo zadá pouze identifikátor a adresu sestaví aplikace sama.
2. **Povolit jen HTTPS:** Jiné protokoly jako `file`, `gopher` nebo `ftp` v cURL vyloučit, i pro přesměrování.
3. **Nenásledovat přesměrování:** Povolená adresa může jinak přesměrovat na interní cíl.
4. **Zkontrolovat a zafixovat přeloženou adresu:** Přeložit název hostitele, zkontrolovat IP proti privátním rozsahům, loopbacku a rozsahům link-local a použít přesně tuto IP pro spojení. Jinak může druhé překládání DNS vrátit jiný cíl.
5. **Omezit síť:** Webserver smí navenek dosáhnout jen cílů, které potřebuje.

## Bezpečný vzor s cURL

Následující příklad zavádí kroky 1 až 4 pro IPv4. Názvy hostitelů a postup jsou příklady; pro IPv6 platí stejné kontroly.

```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;
}
```

Některé podrobnosti jsou důležité. `gethostbyname()` při chybě vrací název hostitele; následná kontrola IP to zachytí. Příznaky `FILTER_FLAG_NO_PRIV_RANGE` a `FILTER_FLAG_NO_RES_RANGE` vylučují podle příručky PHP mimo jiné rozsahy 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8 a 169.254.0.0/16. Od PHP 8.2 existuje navíc `FILTER_FLAG_GLOBAL_RANGE`, který připouští jen globálně platné adresy. `CURLOPT_RESOLVE` zajistí, že cURL použije přesně zkontrolovanou adresu, zatímco název hostitele a ověření certifikátu zůstanou zachovány.

Dva další body omezují, co se útočník ze zranitelnosti SSRF dozví. Odpovědi a chybová hlášení stahovaného cíle nevracejte uživateli nefiltrovaně: už rozlišení mezi „spojení odmítnuto“ a „vypršení času“ prozradí, jaké interní služby existují. A omezte dobu běhu a velikost odpovědi, aby funkce nešla použít k přetížení vlastního serveru. Webhooky na adresy, které si zákazníci zadávají sami, nejlépe odesílejte z fronty, ne během probíhajícího požadavku.

> **Důležité:**
> Pokud vaše aplikace opravdu musí stahovat libovolné adresy, například pro náhledy odkazů, přesuňte tuto funkci do samostatné služby bez přístupu do vnitřní sítě. Seznam povolených hodnot pak není možný, tím důležitější je oddělení sítě.

## Síťová úroveň jako záchranná síť

Kontroly v kódu mohou mít mezery, například když dvě knihovny rozloží URL různě. OWASP radí takové nesrovnalosti řešit odmítnutím a navíc omezovat na úrovni sítě. Webserver potřebuje navenek většinou jen málo cílů: zdroje balíčků, platební a dopravní služby, poštovní relay, určitá rozhraní.

Firewall pro odchozí provoz pomáhá dvojnásob. Zastaví požadavky SSRF do vnitřní sítě a po průniku, například přes nezáplatovanou verzi PHP, ztěžuje stahování dalších nástrojů. Jak sestavit pravidla, popisuje článek [Firewall pro webserver: otevřít jen nutné porty](https://phpsos.de/cs/blog/firewall-fuer-webserver/). Jaká spojení server skutečně vytváří, ukazuje [Kontrola odchozích spojení serveru](https://phpsos.de/cs/blog/ausgehende-verbindungen-pruefen/).

V cloudových prostředích patří k tomu zpevněná varianta služby metadat. OWASP uvádí pro AWS přechod na IMDSv2 a vypnutí IMDSv1.

## Jak SSRF vyhledat ve stávajícím kódu

Rychlým začátkem je vyhledání míst, kde funkce pro stahování dostávají hodnoty z požadavku:

```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"
```

Nálezy jsou úkoly ke kontrole. Obzvlášť důkladně se podívejte na funkce, které používají administrátoři: importy, nastavení webhooků, kontroly aktualizací pluginů.

Snadno se přehlédnou generátory PDF, které převádějí HTML s vloženými obrázky nebo stylopisy na dokumenty. Pokud část tohoto HTML pochází z uživatelských vstupů, například u faktur s volitelným logem, generátor za určitých okolností stahuje adresy, které určí uživatel. Zkontrolujte tam nastavení pro načítání vzdálených zdrojů a vypněte ho, pokud není potřeba.

## Jak konkrétně pomáhá PHP SOS?

- Najdeme všechna místa, kde vaše aplikace stahuje adresy, i v pluginech a knihovnách.
- Zavedeme centrální funkci pro stahování se seznamem povolených hodnot, omezením protokolů a kontrolou IP.
- Stanovíme pravidla pro odchozí provoz a ochráníme služby metadat v cloudových prostředích.
- Provedení ověříme zvenčí a zdokumentujeme povolené cíle.

Nezávislou kontrolu vaší aplikace popisuje [bezpečnostní audit a penetrační test](https://phpsos.de/cs/leistungen/sicherheitsaudit-penetrationstest/).


## Časté dotazy

**Které funkce PHP lze zneužít pro SSRF?**  
Všechny, které stahují adresu z uživatelských vstupů: cURL, file_get_contents a fopen s obálkami URL, funkce pro obrázky a XML, které načítají vzdálené soubory, i knihovny pro webhooky, kanály nebo náhledy odkazů.

**Stačí zablokovat interní adresy IP?**  
Samotné ne. OWASP označuje černé listiny za snadno obejitelné a doporučuje seznamy povolených hodnot. Aplikace navíc musí použít přesně tu adresu, kterou zkontrolovala, a nesmí následovat přesměrování.

**Proč je SSRF v cloudu obzvlášť kritické?**  
Poskytovatelé cloudu zpřístupňují instancím interní služby metadat, přes které lze částečně získat přístupové údaje. Pokud může aplikace posílat požadavky tam, může se útočník přes ni k těmto datům dostat. Poskytovatelé nabízejí zpevněné varianty těchto služeb, které by měly být aktivní.

**Co přináší firewall pro odchozí provoz?**  
Omezuje, jaké cíle může server vůbec dosáhnout. I když kontrola v kódu selže, zůstanou interní systémy nedostupné. Navíc po průniku ztěžuje stahování škodlivého kódu a odliv dat.

## Zdroje

- [OWASP Server-Side Request Forgery Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)
- [Příručka PHP: konstanty filtrů](https://www.php.net/manual/de/filter.constants.php)
- [Příručka PHP: konstanty cURL](https://www.php.net/manual/de/curl.constants.php)
