# Spriječiti Server-Side Request Forgery (SSRF) u PHP-u

> Spriječiti SSRF u PHP-u: popisi dopuštenog, samo HTTPS, bez preusmjeravanja, provjera razriješene IP adrese i ograničenje odlaznog prometa. S cURL kodom.

Izvor: https://phpsos.de/hr/blog/ssrf-in-php/  
Stanje: 25.10.2026  
Pružatelj: PHP SOS, PHP hitna pomoć i sigurnost poslužitelja. Hitna pomoć putem WhatsAppa: https://wa.me/491736494914?text=Pozdrav%20PHP%20SOS%2C%20trebamo%20pomo%C4%87%20s%20na%C5%A1im%20PHP%20poslu%C5%BEiteljem.%20Tvrtka%3A%20%E2%80%A6%20Web-stranica%3A%20%E2%80%A6

**Ukratko:** SSRF u PHP-u sprječavate tako da Vaša aplikacija dohvaća samo adrese s fiksnog popisa dopuštenog, isključivo preko HTTPS-a, bez praćenja preusmjeravanja. Razriješena IP adresa provjerava se prema privatnim i rezerviranim rasponima, a zatim se zadržava za vezu. Vatrozid za odlazni promet osigurava da interni sustavi ostanu nedostupni čak i uz pogrešku u kodu.

## Kad vlastiti poslužitelj šalje zahtjeve za strance

Pregled poveznice u chatu, uvoz slike proizvoda putem URL-a, webhookovi prema partnerskim sustavima, čitač feedova u CMS-u: mnoge PHP aplikacije dohvaćaju adrese koje je naveo korisnik. To je korisna funkcija i istodobno vrata prema unutra.

Kod Server-Side Request Forgeryja, skraćeno SSRF, napadač navodi aplikaciju da pošalje zahtjev prema odredištu po njegovu izboru. Kvaka: zahtjev dolazi sa samog poslužitelja. Time dosiže sustave koji su izvana blokirani, primjerice baze podataka, upravljačka sučelja u internoj mreži, usluge koje slušaju samo na localhostu ili usluge metapodataka pružatelja oblaka.

U OWASP Top 10 iz 2021. SSRF je bio zasebna kategorija. U PHP projektima ranjivost se često pojavljuje tamo gdje je netko brzo napravio funkciju „dodaj sliku putem URL-a“.

## SSRF u PHP-u: koje funkcije dohvaćaju adrese

Adrese ne dohvaća samo cURL. Obratite pažnju na sljedeća mjesta:

| Funkcija ili područje | Zašto je relevantno |
|---|---|
| cURL (`curl_exec()`) | najčešći put, mogući mnogi protokoli |
| `file_get_contents()`, `fopen()`, `copy()` s URL-om | koriste URL wrappere ako je `allow_url_fopen` aktivan |
| `getimagesize()` i biblioteke za slike | mogu učitavati udaljene datoteke |
| obrada XML-a, SOAP klijenti | pod određenim uvjetima učitavaju vanjske resurse |
| HTTP biblioteke poput Guzzlea | temelje se na cURL-u ili streamovima, ista pravila |

Ako Vaša aplikacija ne mora otvarati udaljene datoteke funkcijama za datoteke, isključite `allow_url_fopen` na strani poslužitelja. Srodnu temu, uključivanje datoteka putem putanja iz zahtjeva, obrađuje vodič [Spriječiti Local i Remote File Inclusion](https://phpsos.de/hr/ratgeber/lfi-rfi/).

## Obrana u pet koraka

OWASP preporučuje kombinaciju mjera koje bi se pojedinačno mogle zaobići, ali zajedno drže:

1. **Popis dopuštenog umjesto slobodnog URL-a:** najbolje je da korisnik bira samo između poznatih odredišta ili da navodi samo identifikator, a aplikacija sama gradi adresu.
2. **Dopustiti samo HTTPS:** druge protokole poput `file`, `gopher` ili `ftp` u cURL-u isključiti, i za preusmjeravanja.
3. **Ne pratiti preusmjeravanja:** dopuštena adresa inače može preusmjeriti na interno odredište.
4. **Provjeriti i zadržati razriješenu adresu:** razriješiti naziv hosta, provjeriti IP prema privatnim rasponima, rasponu loopbacka i link-locala te upravo tu IP koristiti za vezu. Inače drugo DNS razrješenje može dati drugo odredište.
5. **Ograničiti mrežu:** web-poslužitelj prema van smije dosezati samo odredišta koja treba.

## Siguran uzorak s cURL-om

Sljedeći primjer provodi korake 1 do 4 za IPv4. Nazivi hostova i tijek su primjeri; za IPv6 vrijede iste provjere.

```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;
}
```

Nekoliko je detalja važno. `gethostbyname()` pri pogrešci vraća naziv hosta; naknadna provjera IP-a to hvata. Zastavice `FILTER_FLAG_NO_PRIV_RANGE` i `FILTER_FLAG_NO_RES_RANGE` prema PHP priručniku među ostalim isključuju raspone 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8 i 169.254.0.0/16. Od PHP-a 8.2 postoji i `FILTER_FLAG_GLOBAL_RANGE`, koja dopušta samo globalno valjane adrese. `CURLOPT_RESOLVE` osigurava da cURL koristi upravo provjerenu adresu, dok naziv hosta i provjera certifikata ostaju sačuvani.

Dvije dodatne točke ograničavaju što napadač iz SSRF ranjivosti može saznati. Odgovore i poruke o pogreškama dohvaćenog odredišta ne vraćajte korisniku nefiltrirane: već razlika između „veza odbijena“ i „istek vremena“ otkriva koje interne usluge postoje. I ograničite trajanje i veličinu odgovora, kako funkcija ne bi mogla poslužiti za preopterećenje vlastitog poslužitelja. Webhookove prema adresama koje kupci sami upisuju najbolje je slati iz reda čekanja, a ne tijekom tekućeg zahtjeva.

> **Važno:**
> Ako Vaša aplikacija doista mora dohvaćati proizvoljne adrese, primjerice za preglede poveznica, tu funkciju izdvojite u zasebnu uslugu bez pristupa internoj mreži. Popis dopuštenog tada nije moguć, a odvajanje mreže je tim važnije.

## Mrežna razina kao sigurnosna mreža

Provjere u kodu mogu imati rupe, primjerice kad dvije biblioteke URL rastavljaju različito. OWASP savjetuje takve nepodudarnosti tretirati kao odbijanje i dodatno ograničavati na razini mreže. Web-poslužitelj prema van obično treba samo nekoliko odredišta: izvore paketa, pružatelje plaćanja i dostave, mail relay, određena sučelja.

Vatrozid za odlazni promet pomaže dvostruko. Zaustavlja SSRF zahtjeve prema internoj mreži, a nakon provale, primjerice preko nezakrpane PHP verzije, otežava naknadno učitavanje daljnjih alata. Kako izgraditi pravila, opisuje [Vatrozid za web-poslužitelje: otvoriti samo nužne portove](https://phpsos.de/hr/blog/firewall-fuer-webserver/). Koje veze poslužitelj doista uspostavlja, pokazuje [Provjeriti odlazne veze poslužitelja](https://phpsos.de/hr/blog/ausgehende-verbindungen-pruefen/).

U okruženjima oblaka tu spada i ojačana varijanta usluge metapodataka. OWASP za AWS navodi prelazak na IMDSv2 i isključivanje IMDSv1.

## Pronaći SSRF u postojećem kodu

Brz početak je potraga za mjestima na kojima funkcije za dohvat primaju vrijednosti iz zahtjeva:

```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"
```

Pogodci su zadaci za provjeru. Osobito temeljito pogledajte funkcije koje koriste administratori: uvoze, postavke webhookova, provjere updatea dodataka.

Lako se previde generatori PDF-ova koji HTML s ugrađenim slikama ili stilovima pretvaraju u dokumente. Ako dio tog HTML-a potječe iz unosa korisnika, primjerice kod računa sa slobodno odabranim logotipom, generator pod određenim uvjetima dohvaća adrese koje određuje korisnik. Tamo provjerite postavke za učitavanje udaljenih resursa i isključite ga ako nije potrebno.

## Kako PHP SOS konkretno pomaže?

- Pronalazimo sva mjesta na kojima Vaša aplikacija dohvaća adrese, i u dodacima i bibliotekama.
- Gradimo središnju funkciju za dohvat s popisom dopuštenog, ograničenjem protokola i provjerom IP-a.
- Utvrđujemo pravila za odlazni promet i štitimo usluge metapodataka u okruženjima oblaka.
- Provjeravamo provedbu izvana i dokumentiramo dopuštena odredišta.

Neovisnu provjeru Vaše aplikacije opisuje [Sigurnosni audit i penetracijski test](https://phpsos.de/hr/leistungen/sicherheitsaudit-penetrationstest/).


## Česta pitanja

**Koje se PHP funkcije mogu zloupotrijebiti za SSRF?**  
Sve koje dohvaćaju adresu iz unosa korisnika: cURL, file_get_contents i fopen s URL wrapperima, funkcije za slike i XML koje učitavaju udaljene datoteke te biblioteke za webhookove, feedove ili preglede poveznica.

**Je li dovoljno blokirati interne IP adrese?**  
Samo to nije. OWASP crne popise smatra podložnima zaobilaženju i preporučuje popise dopuštenog. Uz to aplikacija mora koristiti upravo onu adresu koju je provjerila i ne smije pratiti preusmjeravanja.

**Zašto je SSRF u oblaku osobito kritičan?**  
Pružatelji oblaka instancama stavljaju na raspolaganje interne usluge metapodataka preko kojih se djelomično mogu dohvatiti pristupni podaci. Ako aplikacija može slati zahtjeve tamo, napadač preko nje može doći do tih podataka. Pružatelji nude ojačane varijante tih usluga koje bi trebale biti uključene.

**Što donosi vatrozid za odlazni promet?**  
Ograničava koja odredišta poslužitelj uopće može dosegnuti. Čak i ako provjera u kodu zakaže, interni sustavi tada ostaju nedostupni. Uz to nakon provale otežava naknadno učitavanje zlonamjernog koda i odljev podataka.

## Izvori

- [OWASP Server-Side Request Forgery Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)
- [PHP priručnik: konstante filtara](https://www.php.net/manual/de/filter.constants.php)
- [PHP priručnik: cURL konstante](https://www.php.net/manual/de/curl.constants.php)
