Kort sagt: SSRF i PHP förhindrar ni genom att er applikation bara hämtar adresser som står på en fast tillåtelselista, uteslutande via HTTPS och utan att följa omdirigeringar. Den upplösta IP-adressen kontrolleras mot privata och reserverade områden och hålls sedan fast för anslutningen. En brandvägg för utgående trafik ser till att interna system förblir onåbara även vid ett fel i koden.
När den egna servern ställer förfrågningar åt främlingar
Länkförhandsvisningar i en chatt, import av en produktbild via URL, webhooks till partnersystem, en flödesläsare i CMS:et: Många PHP-applikationer hämtar adresser som en användare har angett. Det är en nyttig funktion och samtidigt en dörr inåt.
Vid Server-Side Request Forgery, kort SSRF, får en angripare applikationen att skicka en förfrågan till ett mål efter eget val. Poängen: Förfrågan kommer från servern själv. Den når därmed system som är spärrade utifrån, till exempel databaser, administrationsgränssnitt i det interna nätet, tjänster som bara lyssnar på localhost eller molnleverantörers metadatatjänster.
I OWASP Top 10 från 2021 var SSRF en egen kategori. I PHP-projekt dyker sårbarheten ofta upp där någon snabbt har byggt en funktion för att ”lägga till bild via URL”.
SSRF i PHP: vilka funktioner som hämtar adresser
Det är inte bara cURL som hämtar adresser. Följande ställen bör ni ha uppsikt över:
| Funktion eller område | Varför relevant |
|---|---|
cURL (curl_exec()) |
vanligaste vägen, många protokoll möjliga |
file_get_contents(), fopen(), copy() med URL |
använder URL-wrappers när allow_url_fopen är aktivt |
getimagesize() och bildbibliotek |
kan ladda fjärrfiler |
| XML-bearbetning, SOAP-klienter | laddar under vissa omständigheter externa resurser |
| HTTP-bibliotek som Guzzle | bygger på cURL eller streams, samma regler |
Behöver er applikation inte öppna fjärrfiler via filfunktioner, stäng av allow_url_fopen på serversidan. Ett närliggande ämne, inkludering av filer via sökvägar från förfrågan, behandlar guiden Förhindra local och remote file inclusion.
Försvaret i fem steg
OWASP rekommenderar en kombination av åtgärder som var för sig skulle kunna kringgås men tillsammans håller:
- Tillåtelselista i stället för fri URL: Bäst är att användaren bara väljer bland kända mål, eller bara anger en identifierare, och applikationen bygger adressen själv.
- Tillåt bara HTTPS: Uteslut andra protokoll som
file,gopherellerftpi cURL, även för omdirigeringar. - Följ inga omdirigeringar: En tillåten adress kan annars omdirigera till ett internt mål.
- Kontrollera och håll fast den upplösta adressen: Slå upp värdnamnet, kontrollera IP-adressen mot privata områden, loopback och link-local-områden och använd exakt den IP-adressen för anslutningen. Annars kan en andra DNS-uppslagning ge ett annat mål.
- Begränsa nätverket: Webbservern får nå utåt bara de mål den behöver.
Ett säkert mönster med cURL
Följande exempel genomför steg 1 till 4 för IPv4. Värdnamn och förlopp är exempel; för IPv6 gäller samma kontroller.
<?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ågra detaljer är viktiga. gethostbyname() returnerar vid fel värdnamnet; den efterföljande IP-kontrollen fångar upp det. Flaggorna FILTER_FLAG_NO_PRIV_RANGE och FILTER_FLAG_NO_RES_RANGE utesluter enligt PHP-manualen bland annat områdena 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8 och 169.254.0.0/16. Sedan PHP 8.2 finns dessutom FILTER_FLAG_GLOBAL_RANGE, som bara tillåter globalt giltiga adresser. CURLOPT_RESOLVE ser till att cURL använder exakt den kontrollerade adressen, medan värdnamn och certifikatkontroll bevaras.
Ytterligare två punkter begränsar vad en angripare lär sig av en SSRF-sårbarhet. Returnera inte det hämtade målets svar och felmeddelanden ofiltrerat till användaren: Redan skillnaden mellan ”anslutning nekad” och ”tidsgräns överskriden” avslöjar vilka interna tjänster som finns. Och begränsa körtid och storlek på svaret så att funktionen inte kan användas för att överbelasta den egna servern. Webhooks till adresser som kunder själva anger skickar ni bäst från en kö och inte under den pågående förfrågan.
Måste er applikation verkligen hämta godtyckliga adresser, till exempel för länkförhandsvisningar, lägg ut den funktionen i en separat tjänst utan tillträde till det interna nätet. En tillåtelselista är då inte möjlig, nätseparationen desto viktigare.
Nätverksnivån som skyddsnät
Kodkontroller kan ha luckor, till exempel när två bibliotek delar upp en URL olika. OWASP råder att behandla sådana inkonsekvenser som avvisning och dessutom begränsa på nätverksnivå. En webbserver behöver utåt oftast bara få mål: paketkällor, betal- och fraktleverantörer, e-postrelä, bestämda gränssnitt.
En brandvägg för utgående trafik hjälper dubbelt. Den stoppar SSRF-förfrågningar in i det interna nätet, och den försvårar efter ett intrång, till exempel via en opatchad PHP-version, att ytterligare verktyg laddas ned. Hur ni bygger upp reglerna beskriver Brandvägg för webbservrar: öppna bara nödvändiga portar. Vilka anslutningar en server faktiskt bygger upp visar Kontrollera en servers utgående anslutningar.
I molnmiljöer hör den härdade varianten av metadatatjänsten dit. OWASP nämner för AWS övergången till IMDSv2 och avstängning av IMDSv1.
Spåra upp SSRF i befintlig kod
En snabb ingång är sökningen efter ställen där hämtningsfunktioner får värden från förfrågan:
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"
Träffar är kontrolluppdrag. Särskilt grundligt bör ni titta på funktioner som administratörer använder: importer, webhook-inställningar, uppdateringskontroller för tillägg.
Lätt att förbise är PDF-generatorer som omvandlar HTML med inbäddade bilder eller stilmallar till dokument. Kommer en del av den HTML-koden från användarinmatning, till exempel vid fakturor med fritt valbar logotyp, hämtar generatorn under vissa omständigheter adresser som användaren bestämmer. Kontrollera där inställningarna för att ladda fjärrresurser och stäng av det om det inte behövs.
Hur hjälper PHP SOS konkret?
- Vi hittar alla ställen där er applikation hämtar adresser, även i tillägg och bibliotek.
- Vi bygger en central hämtningsfunktion med tillåtelselista, protokollbegränsning och IP-kontroll.
- Vi fastställer regler för utgående trafik och skyddar metadatatjänster i molnmiljöer.
- Vi kontrollerar genomförandet utifrån och dokumenterar tillåtna mål.
En oberoende granskning av er applikation beskriver säkerhetsaudit och penetrationstest.
Vanliga frågor
Vilka PHP-funktioner kan missbrukas för SSRF?
Alla som hämtar en adress från användarinmatning: cURL, file_get_contents och fopen med URL-wrappers, bild- och XML-funktioner som laddar fjärrfiler samt bibliotek för webhooks, flöden eller länkförhandsvisningar.
Räcker det att spärra interna IP-adresser?
Inte ensamt. OWASP betecknar spärrlistor som lätta att kringgå och rekommenderar tillåtelselistor. Dessutom måste applikationen använda exakt den adress den har kontrollerat och får inte följa omdirigeringar.
Varför är SSRF särskilt kritiskt i molnet?
Molnleverantörer ger instanser interna metadatatjänster via vilka inloggningsuppgifter delvis går att hämta. Kan en applikation skicka förfrågningar dit kan en angripare nå dessa data genom den. Leverantörerna erbjuder härdade varianter av dessa tjänster som bör vara aktiverade.
Vad ger en brandvägg för utgående trafik?
Den begränsar vilka mål servern över huvud taget kan nå. Även om en kontroll i koden felar förblir interna system då onåbara. Dessutom försvårar den efter ett intrång att skadlig kod laddas ned och att data läcker ut.