Trumpai: SSRF PHP užkertate kelią taip, kad jūsų programa kreiptųsi tik į adresus, esančius fiksuotame leidžiamųjų sąraše, tik per HTTPS ir nesekdama nukreipimų. Išspręstas IP adresas tikrinamas pagal privačius ir rezervuotus intervalus ir tada ryšiui užfiksuojamas. Ugniasienė išeinančiam srautui pasirūpina, kad vidinės sistemos net ir kodo klaidos atveju liktų nepasiekiamos.
Kai jūsų serveris siunčia užklausas svetimiems
Nuorodų peržiūros pokalbyje, produkto paveikslėlio importas pagal URL, webhook partnerių sistemoms, srautų (feed) skaitytuvas CMS: daugelis PHP programų kreipiasi į adresus, kuriuos nurodė naudotojas. Tai naudinga funkcija ir kartu durys į vidų.
Su Server-Side Request Forgery, trumpai SSRF, užpuolikas privers programą išsiųsti užklausą jo pasirinktam tikslui. Esmė: užklausa ateina iš paties serverio. Todėl ji pasiekia sistemas, kurios iš išorės užrakintos, pavyzdžiui, duomenų bazes, administravimo sąsajas vidiniame tinkle, tarnybas, klausančias tik localhost, ar debesies teikėjų metaduomenų tarnybas.
2021 m. OWASP Top 10 SSRF buvo atskira kategorija. PHP projektuose spraga dažnai atsiranda ten, kur kažkas greitai sukūrė funkciją „pridėti paveikslėlį pagal URL“.
SSRF PHP: kurios funkcijos kreipiasi į adresus
Adresų kviečia ne tik cURL. Į šias vietas verta atkreipti dėmesį:
| Funkcija ar sritis | Kodėl svarbu |
|---|---|
cURL (curl_exec()) |
dažniausias kelias, galimi daugelis protokolų |
file_get_contents(), fopen(), copy() su URL |
naudoja URL apvalkalus (wrapper), jei aktyvus allow_url_fopen |
getimagesize() ir paveikslėlių bibliotekos |
gali įkelti nuotolinius failus |
| XML apdorojimas, SOAP klientai | tam tikromis aplinkybėmis kraunami išoriniai ištekliai |
| HTTP bibliotekos kaip Guzzle | remiasi cURL arba srautais, tos pačios taisyklės |
Jei jūsų programai nereikia atidaryti nuotolinių failų per failų funkcijas, išjunkite allow_url_fopen serverio pusėje. Giminingą temą, failų įtraukimą pagal kelius iš užklausos, nagrinėja vadovas Local ir Remote File Inclusion prevencija.
Gynyba penkiais žingsniais
OWASP rekomenduoja priemonių derinį, kurias pavienes galima apeiti, bet kartu jos laiko:
- Leidžiamųjų sąrašas vietoj laisvo URL: geriausia, kad naudotojas rinktųsi tik iš žinomų tikslų arba nurodytų tik identifikatorių, o adresą programa sudarytų pati.
- Leisti tik HTTPS: cURL išjungti kitus protokolus kaip
file,gopherarftp, taip pat nukreipimams. - Nesekti nukreipimų: kitaip leidžiamas adresas gali nukreipti į vidinį tikslą.
- Patikrinti ir užfiksuoti išspręstą adresą: išspręsti pagrindinio kompiuterio vardą, patikrinti IP pagal privačius, loopback ir link-local intervalus ir ryšiui naudoti būtent šį IP. Kitaip antras DNS sprendimas gali pateikti kitą tikslą.
- Apriboti tinklą: žiniatinklio serveris į išorę gali pasiekti tik tuos tikslus, kurių jam reikia.
Saugus šablonas su cURL
Toliau pateiktas pavyzdys įgyvendina 1–4 žingsnius IPv4. Pagrindinių kompiuterių vardai ir eiga yra pavyzdžiai; IPv6 galioja tos pačios patikros.
<?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;
}
Keletas detalių svarbu. gethostbyname() klaidos atveju grąžina pagrindinio kompiuterio vardą; vėlesnė IP patikra tai sučiumpa. Vėliavėlės FILTER_FLAG_NO_PRIV_RANGE ir FILTER_FLAG_NO_RES_RANGE, pasak PHP vadovo, be kita ko, atmeta intervalus 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8 ir 169.254.0.0/16. Nuo PHP 8.2 yra ir FILTER_FLAG_GLOBAL_RANGE, leidžianti tik globaliai galiojančius adresus. CURLOPT_RESOLVE pasirūpina, kad cURL naudotų būtent patikrintą adresą, o pagrindinio kompiuterio vardas ir sertifikato patikra išliktų.
Dar du dalykai riboja, ką užpuolikas sužino iš SSRF spragos. Nekeiskite pakviesto tikslo atsakymų ir klaidų pranešimų nefiltruotų naudotojui: jau skirtumas tarp „ryšys atmestas“ ir „laiko riba viršyta“ išduoda, kokios vidinės tarnybos egzistuoja. Ir apribokite atsakymo trukmę bei dydį, kad funkcija netiktų savo serverio perkrovimui. Webhook adresams, kuriuos klientai įrašo patys, geriausia siųsti iš eilės, o ne vykstant užklausai.
Jei jūsų programai tikrai reikia kviesti savavališkus adresus, pavyzdžiui, nuorodų peržiūroms, iškelkite šią funkciją į atskirą tarnybą be prieigos prie vidinio tinklo. Leidžiamųjų sąrašas tada neįmanomas, o tinklo atskyrimas tuo svarbesnis.
Tinklo lygmuo kaip saugumo tinklas
Kodo patikros gali turėti spragų, pavyzdžiui, kai dvi bibliotekos URL išskaido skirtingai. OWASP pataria tokius neatitikimus traktuoti kaip atmetimą ir papildomai apriboti tinklo lygmenyje. Žiniatinklio serveriui į išorę dažniausiai reikia tik kelių tikslų: paketų šaltinių, mokėjimo ir siuntų paslaugų teikėjų, pašto relay, tam tikrų sąsajų.
Ugniasienė išeinančiam srautui padeda dvigubai. Ji sustabdo SSRF užklausas į vidinį tinklą ir apsunkina po įsilaužimo, pavyzdžiui, per nepataisytą PHP versiją, papildomų įrankių parsisiuntimą. Kaip sudaryti taisykles, aprašo Ugniasienė žiniatinklio serveriui: atverti tik reikalingus prievadus. Kokius ryšius serveris iš tikrųjų užmezga, parodo Serverio išeinančių ryšių patikra.
Debesies aplinkose prie to priklauso sutvirtintas metaduomenų tarnybos variantas. OWASP AWS atveju nurodo perėjimą prie IMDSv2 ir IMDSv1 išjungimą.
SSRF paieška esamame kode
Greitas įėjimas yra vietų paieška, kuriose kreipimosi funkcijos gauna reikšmes iš užklausos:
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"
Radiniai yra patikros užduotys. Ypač kruopščiai peržiūrėkite funkcijas, kuriomis naudojasi administratoriai: importus, webhook nustatymus, papildinių atnaujinimų patikras.
Lengvai praleidžiami PDF generatoriai, paverčiantys HTML su įterptais paveikslėliais ar stiliaus lentelėmis dokumentais. Jei dalis šio HTML kilusi iš naudotojo įvesties, pavyzdžiui, sąskaitose su laisvai pasirenkamu logotipu, generatorius gali kviesti naudotojo nustatytus adresus. Ten patikrinkite nuotolinių išteklių įkėlimo nustatymus ir išjunkite jį, jei nereikalingas.
Kaip PHP SOS padeda konkrečiai?
- Randame visas vietas, kuriose jūsų programa kviečia adresus, taip pat papildiniuose ir bibliotekose.
- Sukuriame centrinę kvietimo funkciją su leidžiamųjų sąrašu, protokolų apribojimu ir IP patikra.
- Nustatome išeinančio srauto taisykles ir apsaugome metaduomenų tarnybas debesies aplinkose.
- Įgyvendinimą patikriname iš išorės ir dokumentuojame leidžiamus tikslus.
Nepriklausomą jūsų programos patikrą aprašo Saugumo auditas ir įsiskverbimo testas.
Dažni klausimai
Kurios PHP funkcijos gali būti išnaudotos SSRF?
Visos, kurios kreipiasi į adresą iš naudotojo įvesties: cURL, file_get_contents ir fopen su URL apvalkalais (wrapper), paveikslėlių ir XML funkcijos, kraunančios nuotolinius failus, taip pat bibliotekos webhook, srautams (feed) ar nuorodų peržiūroms.
Ar pakanka užblokuoti vidinius IP adresus?
Vien to ne. OWASP uždraudimo sąrašus vadina lengvai apeinamais ir rekomenduoja leidžiamųjų sąrašus. Be to, programa turi naudoti būtent tą adresą, kurį patikrino, ir negali sekti nukreipimų.
Kodėl SSRF debesyje ypač kritiška?
Debesies teikėjai instancijoms suteikia vidines metaduomenų tarnybas, per kurias kartais galima gauti prisijungimo duomenis. Jei programa gali ten siųsti užklausas, užpuolikas per ją gali pasiekti šiuos duomenis. Teikėjai siūlo sutvirtintus šių tarnybų variantus, kuriuos reikėtų įjungti.
Ką duoda ugniasienė išeinančiam srautui?
Ji apriboja, kokius tikslus serveris apskritai gali pasiekti. Net jei kodo patikra nepavyksta, vidinės sistemos lieka nepasiekiamos. Be to, po įsilaužimo ji apsunkina kenkėjiško kodo parsisiuntimą ir duomenų nutekėjimą.