Ajankohtainen tilanne: PHP2Shell. Kaksi kriittistä PHP-haavoittuvuutta (CVSS 9.2) sallii haittakoodin suorittamisen ilman kirjautumista. Korjaukset 21.10.2026 alkaen.Mitä nyt on tehtävä →

Server-Side Request Forgeryn (SSRF) estäminen PHP:ssä

Miten kehittäjät estävät PHP-sovellusta kysymästä sisäisiä järjestelmiä vieraiden puolesta: sallittujen luettelot, protokollarajoitus, ratkaistun osoitteen tarkistus ja verkon rajat.

Tilanne Lukuaika 5 minTurvallinen kehitys

Lyhyesti: SSRF:n estätte PHP:ssä siten, että sovelluksenne hakee vain osoitteita, jotka ovat kiinteällä sallittujen luettelolla, ainoastaan HTTPS:llä ja seuraamatta uudelleenohjauksia. Ratkaistu IP-osoite tarkistetaan yksityisiä ja varattuja alueita vasten ja lukitaan sitten yhteyttä varten. Lähtevän liikenteen palomuuri pitää sisäiset järjestelmät tavoittamattomina myös koodivirheen sattuessa.

Kun oma palvelin tekee pyyntöjä vieraille

Linkkiesikatselut chatissa, tuotekuvan tuonti URL-osoitteella, webhookit kumppanijärjestelmiin, syötteenlukija CMS:ssä: monet PHP-sovellukset hakevat osoitteita, jotka käyttäjä on antanut. Se on hyödyllinen toiminto ja samalla ovi sisäänpäin.

Server-Side Request Forgeryssa, lyhyesti SSRF, hyökkääjä saa sovelluksen lähettämään pyynnön valitsemaansa kohteeseen. Juju: pyyntö tulee palvelimelta itseltään. Se tavoittaa siten järjestelmiä, jotka on suljettu ulkopuolelta, esimerkiksi tietokantoja, sisäverkon hallintakäyttöliittymiä, palveluja, jotka kuuntelevat vain localhostissa, tai pilvipalvelun tarjoajien metatietopalveluja.

OWASP Top 10:ssä vuodelta 2021 SSRF oli oma kategoriansa. PHP-projekteissa aukko esiintyy usein siellä, missä joku on nopeasti rakentanut toiminnon ”lisää kuva URL-osoitteella”.

SSRF PHP:ssä: mitkä funktiot hakevat osoitteita

Osoitteita ei hae vain cURL. Seuraaviin kohtiin kannattaa kiinnittää huomiota:

Funktio tai alue Miksi merkityksellinen
cURL (curl_exec()) yleisin tie, monet protokollat mahdollisia
file_get_contents(), fopen(), copy() URL-osoitteella käyttävät URL-wrappereita, kun allow_url_fopen on aktiivinen
getimagesize() ja kuvakirjastot voivat ladata etätiedostoja
XML-käsittely, SOAP-asiakkaat lataavat tilanteesta riippuen ulkoisia resursseja
HTTP-kirjastot kuten Guzzle perustuvat cURLiin tai streameihin, samat säännöt

Jos sovelluksenne ei tarvitse avata etätiedostoja tiedostofunktioilla, kytkekää allow_url_fopen pois palvelinpuolella. Aiheeseen liittyvää, tiedostojen sisällyttämistä pyynnön polkujen kautta, käsittelee opas Local ja Remote File Inclusionin estäminen.

Puolustus viidessä vaiheessa

OWASP suosittelee toimenpiteiden yhdistelmää, joista jokainen voitaisiin yksin kiertää, mutta yhdessä ne kantavat:

  1. Sallittujen luettelo vapaan URL-osoitteen sijaan: Parhaiten käyttäjä valitsee vain tunnetuista kohteista tai antaa pelkän tunnisteen, ja sovellus rakentaa osoitteen itse.
  2. Salli vain HTTPS: Sulkekaa cURLissa pois muut protokollat kuten file, gopher tai ftp, myös uudelleenohjauksista.
  3. Älä seuraa uudelleenohjauksia: Sallittu osoite voi muuten ohjata sisäiseen kohteeseen.
  4. Tarkista ja lukitse ratkaistu osoite: Ratkaiskaa isäntänimi, tarkistakaa IP yksityisiä, loopback- ja link-local-alueita vasten ja käyttäkää juuri tätä IP-osoitetta yhteydessä. Muuten toinen DNS-ratkaisu voi tuottaa toisen kohteen.
  5. Rajaa verkko: Verkkopalvelin saa tavoittaa ulospäin vain ne kohteet, joita se tarvitsee.

Turvallinen malli cURLilla

Seuraava esimerkki toteuttaa vaiheet 1–4 IPv4:lle. Isäntänimet ja kulku ovat esimerkkejä; IPv6:lle pätevät samat tarkistukset.

<?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;
}

Muutama yksityiskohta on tärkeä. gethostbyname() palauttaa virheen sattuessa isäntänimen; sitä seuraava IP-tarkistus kaappaa tämän. Liput FILTER_FLAG_NO_PRIV_RANGE ja FILTER_FLAG_NO_RES_RANGE sulkevat PHP-käsikirjan mukaan pois muun muassa alueet 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8 ja 169.254.0.0/16. PHP 8.2:sta alkaen on lisäksi FILTER_FLAG_GLOBAL_RANGE, joka sallii vain globaalisti kelvolliset osoitteet. CURLOPT_RESOLVE huolehtii siitä, että cURL käyttää täsmälleen tarkistettua osoitetta, ja isäntänimi sekä varmenteen tarkistus säilyvät.

Kaksi muuta kohtaa rajaa, mitä hyökkääjä oppii SSRF-aukosta. Älkää palauttako haetun kohteen vastauksia ja virheilmoituksia suodattamattomina käyttäjälle: jo ero ilmoitusten ”yhteys evätty” ja ”aikakatkaisu” välillä paljastaa, mitä sisäisiä palveluja on olemassa. Ja rajatkaa vastauksen kesto ja koko, jottei toiminto kelpaa oman palvelimen ylikuormittamiseen. Webhookit osoitteisiin, jotka asiakkaat itse syöttävät, kannattaa lähettää jonosta eikä käynnissä olevan pyynnön aikana.

Tärkeää

Jos sovelluksenne todella tarvitsee hakea mielivaltaisia osoitteita, esimerkiksi linkkiesikatseluja varten, siirtäkää tämä toiminto erilliseen palveluun, jolla ei ole pääsyä sisäverkkoon. Sallittujen luettelo ei silloin ole mahdollinen, verkon erottelu on sitäkin tärkeämpi.

Verkkotaso turvaverkkona

Koodin tarkistuksissa voi olla aukkoja, esimerkiksi jos kaksi kirjastoa pilkkoo URL-osoitteen eri tavalla. OWASP neuvoo käsittelemään tällaiset epäsuhdat hylkäyksenä ja rajaamaan lisäksi verkkotasolla. Verkkopalvelin tarvitsee ulospäin yleensä vain muutamia kohteita: pakettilähteet, maksu- ja toimituspalvelut, sähköpostireleen ja tietyt rajapinnat.

Lähtevän liikenteen palomuuri auttaa kahdella tavalla. Se pysäyttää SSRF-pyynnöt sisäverkkoon, ja se vaikeuttaa tunkeutumisen jälkeen, esimerkiksi korjaamattoman PHP-version kautta, lisätyökalujen jälkilatausta. Miten säännöt rakennetaan, kuvaa Palomuuri verkkopalvelimelle: avaa vain tarvittavat portit. Mitä yhteyksiä palvelin todella muodostaa, näyttää Palvelimen lähtevien yhteyksien tarkistus.

Pilviympäristöissä kuuluu mukaan metatietopalvelun koventettu versio. OWASP mainitsee AWS:lle siirtymisen IMDSv2:een ja IMDSv1:n kytkemisen pois.

SSRF:n jäljittäminen vanhasta koodista

Nopea lähtökohta on haku kohtiin, joissa hakufunktiot saavat arvoja pyynnöstä:

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"

Osumat ovat tarkistustehtäviä. Erityisen perusteellisesti kannattaa katsoa ylläpitäjien käyttämiä toimintoja: tuonnit, webhook-asetukset, laajennusten päivitystarkistukset.

Helposti jäävät huomaamatta PDF-generaattorit, jotka muuntavat HTML:n upotettuine kuvineen tai tyylitiedostoineen asiakirjoiksi. Jos osa tästä HTML:stä on peräisin käyttäjän syötteistä, esimerkiksi laskuissa vapaasti valittavalla logolla, generaattori hakee tilanteesta riippuen osoitteita, jotka käyttäjä määrää. Tarkistakaa siellä etäresurssien latauksen asetukset ja kytkekää se pois, jos sitä ei tarvita.

Miten PHP SOS auttaa konkreettisesti?

  • Löydämme kaikki kohdat, joissa sovelluksenne hakee osoitteita, myös laajennuksissa ja kirjastoissa.
  • Rakennamme keskitetyn hakufunktion sallittujen luettelolla, protokollarajoituksella ja IP-tarkistuksella.
  • Määrittelemme säännöt lähtevälle liikenteelle ja suojaamme metatietopalvelut pilviympäristöissä.
  • Tarkistamme toteutuksen ulkopuolelta ja dokumentoimme sallitut kohteet.

Sovelluksenne riippumattoman tarkistuksen kuvaa tietoturva-auditointi ja tunkeutumistesti.

Usein kysytyt kysymykset

Mitä PHP-funktioita voi väärinkäyttää SSRF:ään?

Kaikkia, jotka hakevat käyttäjän syötteestä tulevan osoitteen: cURL, file_get_contents ja fopen URL-wrappereilla, kuva- ja XML-funktiot, jotka lataavat etätiedostoja, sekä webhookkien, syötteiden tai linkkiesikatselujen kirjastot.

Riittääkö sisäisten IP-osoitteiden estäminen?

Yksin ei. OWASP kuvaa estolistat kierrettäviksi ja suosittelee sallittujen luetteloita. Lisäksi sovelluksen on käytettävä täsmälleen sitä osoitetta, jonka se on tarkistanut, eikä sen pidä seurata uudelleenohjauksia.

Miksi SSRF on pilvessä erityisen kriittinen?

Pilvipalvelun tarjoajat antavat instansseille sisäisiä metatietopalveluja, joiden kautta osa tunnuksista on haettavissa. Jos sovellus voi lähettää pyyntöjä sinne, hyökkääjä voi tavoittaa nämä tiedot sen kautta. Tarjoajilla on näistä palveluista koventamia versioita, jotka kannattaa ottaa käyttöön.

Mitä hyötyä on lähtevän liikenteen palomuurista?

Se rajaa, mihin kohteisiin palvelin ylipäätään pääsee. Vaikka koodin tarkistus epäonnistuisi, sisäiset järjestelmät pysyvät tavoittamattomina. Lisäksi se vaikeuttaa haittakoodin jälkilatausta ja tietojen vuotamista tunkeutumisen jälkeen.

Lähteet

Pikayhteys

Jokainen tunti on tärkeä. Kirjoittakaa meille nyt.

Kuvatkaa lyhyesti järjestelmä, PHP-versio ja havainnot. Otamme yhteyttä, selvitämme tilanteen ja työskentelemme etänä, kunnes tapaus on käsitelty loppuun ja dokumentoitu.

Pikaapu