Stručne: SSRF v PHP zabránite tak, že vaša aplikácia sťahuje len adresy, ktoré sú na pevnom zozname povolených, výlučne cez HTTPS a bez nasledovania presmerovaní. Preložená adresa IP sa skontroluje voči súkromným a rezervovaným rozsahom a potom sa pre spojenie zafixuje. Firewall pre odchádzajúcu prevádzku zabezpečí, že interné systémy zostanú nedosiahnuteľné aj pri chybe v kóde.
Keď vlastný server posiela požiadavky za cudzích
Náhľady odkazov v chate, import obrázka produktu cez URL, webhooky do partnerských systémov, čítačka kanálov v CMS: mnohé aplikácie PHP sťahujú adresy, ktoré zadal používateľ. Je to užitočná funkcia a zároveň dvere dovnútra.
Pri Server-Side Request Forgery, skrátene SSRF, prinúti útočník aplikáciu poslať požiadavku na cieľ podľa vlastného výberu. Háčik: požiadavka prichádza zo samotného servera. Dosiahne tak systémy, ktoré sú zvonka zablokované, napríklad databázy, správcovské rozhrania vo vnútornej sieti, služby počúvajúce len na localhost alebo služby metadát poskytovateľov cloudu.
V OWASP Top 10 z roku 2021 tvorilo SSRF samostatnú kategóriu. V projektoch PHP sa zraniteľnosť často objavuje tam, kde niekto narýchlo postavil funkciu „pridať obrázok cez URL“.
SSRF v PHP: ktoré funkcie sťahujú adresy
Adresy nesťahuje len cURL. Tieto miesta by ste mali mať na očiach:
| Funkcia alebo oblasť | Prečo je relevantná |
|---|---|
cURL (curl_exec()) |
najčastejšia cesta, možných je veľa protokolov |
file_get_contents(), fopen(), copy() s URL |
používajú wrappery URL, ak je aktívne allow_url_fopen |
getimagesize() a knižnice pre obrázky |
môžu načítať vzdialené súbory |
| spracovanie XML, klienti SOAP | za určitých okolností načítavajú externé zdroje |
| knižnice HTTP ako Guzzle | stavajú na cURL alebo streamoch, rovnaké pravidlá |
Ak vaša aplikácia nemusí otvárať vzdialené súbory cez súborové funkcie, vypnite allow_url_fopen na strane servera. Príbuzná téma, vkladanie súborov cez cesty z požiadavky, rieši sprievodca Ako zabrániť Local a Remote File Inclusion.
Obrana v piatich krokoch
OWASP odporúča kombináciu opatrení, ktoré by sa jednotlivo dali obísť, no spolu držia:
- Zoznam povolených namiesto voľnej URL: najlepšie je, ak používateľ vyberá len spomedzi známych cieľov, alebo zadá iba identifikátor a aplikácia si adresu zostaví sama.
- Povoliť len HTTPS: iné protokoly ako
file,gopheraleboftpv cURL vylúčiť, aj pri presmerovaniach. - Nenasledovať presmerovania: povolená adresa by inak mohla presmerovať na interný cieľ.
- Skontrolovať a zafixovať preloženú adresu: preložiť názov hostiteľa, skontrolovať IP voči súkromným rozsahom, rozsahom loopback a link-local a práve túto IP použiť pre spojenie. Inak môže druhé preloženie DNS vrátiť iný cieľ.
- Obmedziť sieť: webserver smie smerom von dosiahnuť len ciele, ktoré potrebuje.
Bezpečný vzor s cURL
Nasledujúci príklad zavádza kroky 1 až 4 pre IPv4. Názvy hostiteľov a postup sú príklady; pre IPv6 platia rovnaké kontroly.
<?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;
}
Niekoľko detailov je dôležitých. gethostbyname() pri chybe vráti názov hostiteľa; nasledujúca kontrola IP to zachytí. Príznaky FILTER_FLAG_NO_PRIV_RANGE a FILTER_FLAG_NO_RES_RANGE vylučujú podľa príručky PHP okrem iného 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 navyše FILTER_FLAG_GLOBAL_RANGE, ktorý pripúšťa len globálne platné adresy. CURLOPT_RESOLVE zabezpečí, že cURL použije presne skontrolovanú adresu, pričom zostane zachovaný názov hostiteľa aj kontrola certifikátu.
Dva ďalšie body obmedzujú to, čo sa útočník dozvie zo zraniteľnosti SSRF. Odpovede a chybové hlásenia sťahovaného cieľa nevracajte používateľovi nefiltrované: už rozlíšenie medzi „spojenie odmietnuté“ a „prekročenie času“ prezrádza, aké interné služby existujú. A obmedzte trvanie a veľkosť odpovede, aby sa funkcia nedala použiť na preťaženie vlastného servera. Webhooky na adresy, ktoré zákazníci zadávajú sami, najlepšie odosielajte z fronty a nie počas prebiehajúcej požiadavky.
Ak vaša aplikácia skutočne musí sťahovať ľubovoľné adresy, napríklad pre náhľady odkazov, presuňte túto funkciu do oddelenej služby bez prístupu do vnútornej siete. Zoznam povolených potom nie je možný, o to dôležitejšie je oddelenie siete.
Sieťová úroveň ako záchranná sieť
Kontroly v kóde môžu mať medzery, napríklad keď dve knižnice rozložia URL odlišne. OWASP radí takéto nezrovnalosti vyhodnocovať ako odmietnutie a navyše obmedzovať na úrovni siete. Webserver potrebuje smerom von zvyčajne len málo cieľov: zdroje balíkov, poskytovateľov platieb a dopravy, poštový relay, určité rozhrania.
Firewall pre odchádzajúcu prevádzku pomáha dvojnásobne. Zastaví požiadavky SSRF do vnútornej siete a po vlámaní, napríklad cez nezáplatovanú verziu PHP, sťažuje dosťahovanie ďalších nástrojov. Ako pravidlá zostaviť, opisuje Firewall pre webserver: otvoriť len potrebné porty. Aké spojenia server skutočne vytvára, ukazuje Kontrola odchádzajúcich spojení servera.
V cloudových prostrediach patrí k tomu spevnený variant služby metadát. OWASP uvádza pre AWS prechod na IMDSv2 a vypnutie IMDSv1.
Vyhľadanie SSRF v existujúcom kóde
Rýchlym vstupom je hľadanie miest, kde funkcie sťahovania dostávajú hodnoty z požiadavky:
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 sú úlohy na kontrolu. Obzvlášť dôkladne si prezrite funkcie, ktoré používajú administrátori: importy, nastavenia webhookov, kontroly aktualizácií pluginov.
Ľahko sa prehliadnu generátory PDF, ktoré menia HTML so vloženými obrázkami alebo štýlmi na dokumenty. Ak časť tohto HTML pochádza zo vstupov používateľa, napríklad pri faktúrach s voliteľným logom, generátor môže za určitých okolností sťahovať adresy, ktoré určuje používateľ. Skontrolujte tam nastavenia načítavania vzdialených zdrojov a vypnite ho, ak sa nepotrebuje.
Ako konkrétne pomáha PHP SOS?
- Nájdeme všetky miesta, kde vaša aplikácia sťahuje adresy, aj v pluginoch a knižniciach.
- Zostavíme centrálnu funkciu sťahovania so zoznamom povolených, obmedzením protokolov a kontrolou IP.
- Určíme pravidlá pre odchádzajúcu prevádzku a ochránime služby metadát v cloudových prostrediach.
- Zavedenie preveríme zvonka a zdokumentujeme povolené ciele.
Nezávislú kontrolu vašej aplikácie opisuje Bezpečnostný audit a penetračný test.
Časté otázky
Ktoré funkcie PHP sa dajú zneužiť na SSRF?
Všetky, ktoré sťahujú adresu zo vstupov používateľa: cURL, file_get_contents a fopen s wrappermi URL, funkcie pre obrázky a XML, ktoré načítavajú vzdialené súbory, ako aj knižnice pre webhooky, kanály alebo náhľady odkazov.
Stačí zablokovať interné adresy IP?
Samotné nie. OWASP označuje zoznamy zakázaných hodnôt za náchylné na obídenie a odporúča zoznamy povolených. Aplikácia navyše musí použiť presne tú adresu, ktorú skontrolovala, a nesmie nasledovať presmerovania.
Prečo je SSRF v cloude obzvlášť kritické?
Poskytovatelia cloudu sprístupňujú inštanciám interné služby metadát, cez ktoré sa čiastočne dajú získať prístupové údaje. Ak aplikácia môže tam posielať požiadavky, môže sa útočník cez ňu k týmto údajom dostať. Poskytovatelia ponúkajú spevnené varianty týchto služieb, ktoré by mali byť zapnuté.
Čo prinesie firewall pre odchádzajúcu prevádzku?
Obmedzuje, aké ciele môže server vôbec dosiahnuť. Aj keď zlyhá kontrola v kóde, interné systémy zostanú nedosiahnuteľné. Okrem toho po vlámaní sťažuje dosťahovanie škodlivého kódu a odtok údajov.