SSRF: trovarlo e sfruttarlo in applicazioni Laravel con HTTP client e webhook
Il 8 luglio 2025 ho completato un penetration test per una piattaforma SaaS milanese attiva nel settore della gestione relazioni commerciali B2B - 420 clienti paganti, fatturato mensile ricorrente di 52.000 euro, infrastruttura AWS (EC2 + RDS + S3) con deploy Laravel 11 e PostgreSQL 15. L'ingaggio era stato commissionato dal CTO come parte di un'iniziativa di maturità di sicurezza pre-serie A, con obiettivo di identificare vulnerabilità critiche prima del prossimo giro di investimento che avrebbe comportato due diligence tecnica da parte degli investitori. Durante il quarto giorno dell'assessment, analizzando una feature abbastanza banale della piattaforma - la possibilità per il cliente di configurare webhook in uscita verso URL arbitrari quando avvenivano specifici eventi nel suo tenant - ho identificato una vulnerabilità critica di Server-Side Request Forgery (SSRF) con impatto sull'intera infrastruttura AWS della startup.
Il path di exploitation era lineare ma devastante. Il sistema permetteva al cliente di configurare una URL webhook qualunque e il backend applicativo Laravel effettuava richieste HTTP verso quella URL al verificarsi degli eventi. Senza alcuna validazione della URL di destinazione. In meno di 20 minuti dalla scoperta iniziale ho costruito una dimostrazione che permetteva a un cliente autenticato qualsiasi di: recuperare le AWS IAM role credentials dell'istanza EC2 tramite l'endpoint http://169.254.169.254/latest/meta-data/iam/security-credentials/, accedere ai servizi interni della rete AWS privata (Redis cluster, RDS writer endpoint, servizi interni di monitoring), enumerare servizi di rete sulla subnet privata della VPC. Le IAM credentials esfiltrate davano accesso read/write completo al bucket S3 che conteneva backup del database e upload dei file dei clienti - incluse configurazioni, documenti, e allegati di oltre 12.000 utenti finali dei clienti della startup. Il CTO, durante la sessione di debrief con proof-of-concept live, ha descritto quello che vedeva come "il momento peggiore della mia carriera tecnica".
In tre giornate di remediation affiancata al team, abbiamo risolto la vulnerabilità specifica, implementato una URL allow-list validator custom riutilizzabile per tutti i punti della codebase dove il backend effettua richieste verso URL fornite dall'utente, configurato il metadata service dell'EC2 per richiedere IMDSv2 (che previene una classe di attacchi SSRF classici), segmentato la rete VPC per ridurre il raggio di esposizione in caso di future SSRF residue, fatto training del team su questa classe di vulnerabilità. Il cliente ha incluso il report di security assessment nella due diligence pre-serie A. Il round è stato chiuso positivamente a settembre 2025 per 4,2 milioni di euro. Il valore del singolo intervento di remediation - considerando che un'esposizione reale di queste dimensioni in fase di due diligence avrebbe con altissima probabilità bloccato il round - è letteralmente incalcolabile. Il costo consulenziale è stato 11.600 euro.
Questo articolo descrive la vulnerabilità SSRF nelle sue manifestazioni più comuni in applicazioni Laravel moderne, i pattern di sfruttamento, e la remediation strutturale. Il principio guida è uno: SSRF è una vulnerabilità tra le più sottovalutate perché non è "visibile" in un test di sicurezza superficiale - richiede comprensione del contesto infrastrutturale, ma il suo impatto supera praticamente ogni altra classe di vulnerabilità applicativa. In infrastrutture cloud moderne, un SSRF sfruttabile è quasi sempre equivalente a una compromissione completa dell'account cloud.
Cos'è SSRF e perché è particolarmente devastante nelle architetture cloud moderne
Server-Side Request Forgery è una classe di vulnerabilità in cui un attaccante riesce a indurre il backend di un'applicazione a fare richieste HTTP arbitrarie verso destinazioni di sua scelta, sfruttando la fiducia implicita che la rete interna ripone nelle comunicazioni originate dal server stesso. Il pattern è documentato nella cheatsheet ufficiale OWASP dedicata a SSRF e nella OWASP API Security Top 10 come rischio API7 "Server Side Request Forgery" - è esploso come classe di preoccupazione principale con l'avvento delle architetture cloud dove i servizi metadata e le API interne sono tipicamente raggiungibili solo dal backend stesso.
Il motivo per cui SSRF è particolarmente devastante in ambiente cloud si capisce solo comprendendo l'architettura di rete tipica di AWS, Azure, Google Cloud. Un'istanza EC2 (o equivalente) in AWS ha accesso a quattro classi di destinazioni che un attaccante esterno non può raggiungere direttamente. Prima classe: metadata service, disponibile sull'IP link-local 169.254.169.254, che restituisce IAM credentials temporanee, user data della EC2 e altre informazioni sensibili. Seconda classe: servizi interni della VPC privata, come database managed (RDS su IP privati), cache (ElastiCache), message queue (SQS via VPC endpoint) - tutti raggiungibili solo dall'interno del perimetro VPC. Terza classe: infrastructure endpoints interni come load balancer interni, servizi di monitoring, log aggregators. Quarta classe: altre istanze EC2 nella stessa VPC, che spesso hanno security group permissive con la EC2 applicativa.
Un attaccante che sfrutta un SSRF nel backend può, dall'esterno di internet, impersonare il backend nelle sue comunicazioni verso queste quattro classi di destinazioni - effectively ottiene accesso a tutto quello che il backend vede ma che internet non vede. Nel caso del cliente milanese, l'accesso al metadata service via SSRF ha permesso l'esfiltrazione delle IAM credentials, che a sua volta ha dato accesso a tutto il catalogo IAM delle risorse AWS dell'account - un dominio effect completo partendo da una singola vulnerabilità applicativa.
Il pattern di sfruttamento classico di SSRF contro il metadata AWS è:
curl "https://vulnerable-app.example.com/api/webhook-test?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"Se l'app vulnerabile ritorna il contenuto dell'URL destinazione nella sua risposta HTTP (forma "reflective" di SSRF), l'attaccante vede il nome del ruolo IAM. Successivamente:
curl "https://vulnerable-app.example.com/api/webhook-test?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLENAME"Restituisce JSON con AccessKeyId, SecretAccessKey, Token - le credenziali temporanee AWS che l'attaccante può immediatamente usare per accedere a tutti i servizi AWS dell'account con i permessi del ruolo IAM della EC2.
Se stai gestendo un'applicazione PHP in infrastruttura cloud (AWS, Azure, GCP) con funzionalità che richiedono al backend di effettuare chiamate HTTP verso URL fornite dall'utente, nel mio profilo professionale trovi il dettaglio degli assessment di sicurezza cloud e SSRF che ho condotto in contesti PMI italiane, sempre con valutazione sistematica del blast radius in caso di sfruttamento reale.
I tre pattern applicativi più comuni che producono SSRF nelle applicazioni Laravel
Nella mia esperienza di assessment SSRF su circa 25 applicazioni Laravel negli ultimi quattro anni, tre pattern di codice applicativo producono sistematicamente SSRF sfruttabile. Il riconoscimento di questi pattern è il primo passo per un audit sistematico di un codebase esistente.
Pattern 1: Webhook configurabili dall'utente. L'applicazione permette al cliente (tenant) di configurare una URL webhook verso cui il backend invia notifiche o dati al verificarsi di eventi. Il backend usa Http::post($config->webhook_url, $payload) o equivalente direttamente con la URL configurata dall'utente. Nessuna validazione che la URL non punti a risorse interne. Questo è esattamente il pattern del cliente milanese, ed è probabilmente il pattern SSRF più diffuso in applicazioni SaaS B2B.
Pattern 2: Preview di URL / link unfurling. L'applicazione permette all'utente di inserire URL in un campo (messaggio, post, ticket, commento) e il backend recupera metadata della URL (titolo della pagina, immagine di anteprima, descrizione) per mostrarli nel feed. Il fetch della URL avviene senza validazione. Classico pattern in applicazioni di messaggistica o social commerciale.
Pattern 3: Proxy di risorse esterne. L'applicazione agisce da proxy per risorse esterne - ad esempio un modulo di import CSV dove l'utente fornisce l'URL di un CSV remoto e il backend lo scarica e processa. Oppure un modulo di integrazione con API esterne dove l'URL del partner è configurabile.
In tutti e tre i pattern, la remediation di base richiede che la URL di destinazione sia validata prima di eseguire la chiamata HTTP, con criterio di allow-list esplicita (meglio) o deny-list di destinazioni proibite (peggio ma accettabile se ben curata). La documentazione ufficiale di Laravel sul client HTTP descrive le opzioni disponibili, ma non forza alcun pattern di validazione - è responsabilità del developer.
La remediation: URL allow-list validator riutilizzabile
Il pattern di remediation che ho implementato sul cliente milanese è stato la costruzione di un URL Validator Service riutilizzabile in tutti i punti del codice dove il backend effettua chiamate HTTP verso destinazioni potenzialmente controllate dall'utente. L'implementazione in Laravel sfrutta la dependency injection e il pattern di interface binding che ho descritto nel mio articolo sulla dependency injection avanzata PHP 8 per servizi testabili e sostituibili.
Il core del validator esegue cinque controlli sequenziali sulla URL:
namespace App\Infrastructure\Security;
final class ExternalUrlValidator
{
private const BLOCKED_HOSTS = [
'169.254.169.254',
'metadata.google.internal',
'127.0.0.1',
'0.0.0.0',
'localhost',
];
private const BLOCKED_CIDR = [
'10.0.0.0/8',
'172.16.0.0/12',
'192.168.0.0/16',
'169.254.0.0/16',
'fc00::/7',
];
public function validate(string $rawUrl): void
{
$parsed = parse_url($rawUrl);
if ($parsed === false || !isset($parsed['scheme'], $parsed['host'])) {
throw new InvalidUrlException('URL malformed');
}
if (!in_array(strtolower($parsed['scheme']), ['http', 'https'], true)) {
throw new InvalidUrlException('Only http/https schemes allowed');
}
if (in_array(strtolower($parsed['host']), self::BLOCKED_HOSTS, true)) {
throw new InvalidUrlException('Blocked host');
}
$resolvedIp = gethostbyname($parsed['host']);
if ($resolvedIp === $parsed['host']) {
throw new InvalidUrlException('DNS resolution failed');
}
foreach (self::BLOCKED_CIDR as $cidr) {
if ($this->isIpInCidr($resolvedIp, $cidr)) {
throw new InvalidUrlException('Blocked internal IP range');
}
}
}
private function isIpInCidr(string $ip, string $cidr): bool
{
// implementazione dettagliata con ip2long e mask
}
}I cinque controlli coprono: URL ben formata (primo check), solo schemi HTTP/HTTPS (blocca file://, gopher://, ftp:// usati in SSRF avanzati), host non nella lista bloccata esplicitamente (metadata servizi), risoluzione DNS effettiva dell'host (non lascia che un DNS risolva a IP interno), verifica che l'IP risolto non sia in range privato (IPv4 e IPv6). Ogni step è necessario: saltarne uno apre un bypass.
Un aspetto critico, spesso sottovalutato, è il DNS rebinding attack. Un attaccante configura un record DNS che inizialmente risolve a un IP pubblico (passa il validator), poi cambia il record DNS per risolvere a un IP privato interno; quando il backend fa effettivamente la richiesta HTTP, il DNS è già cambiato e l'attacco riesce. La difesa richiede di risolvere la URL una sola volta durante la validazione, fare la chiamata HTTP all'IP risolto (non al hostname), e passare l'Host header manualmente. Questa tecnica è più complessa della validazione di base ma è necessaria per SSRF-proof completa. Librerie come safecurl/safecurl implementano questo pattern per te.
Configurazione network a livello di infrastruttura: difesa in profondità
Oltre alla remediation a livello applicativo, configurazioni a livello di infrastruttura aggiungono livelli di difesa in profondità che riducono l'impatto di SSRF residui. Primo livello: IMDSv2 obbligatorio su AWS EC2. La versione v2 del metadata service richiede un token di sessione PUT prima di restituire dati; questo impedisce attacchi SSRF semplici che eseguono solo GET. Il cambio da IMDSv1 a IMDSv2 richiede una modifica di configurazione EC2 e aggiornamento delle librerie AWS SDK per supportare il nuovo flusso. Tutte le istanze che creo nuove le configuro sempre con IMDSv2 obbligatorio.
Secondo livello: segmentazione VPC con security group restrittivi. Le istanze applicative dovrebbero poter raggiungere solo i servizi specifici di cui hanno bisogno (database specifico, cache specifica, S3 via VPC endpoint), non avere accesso di rete libero a qualunque altra istanza o servizio nella VPC. Security group restrittive riducono il blast radius in caso di SSRF residuo - l'attaccante che sfrutta SSRF può solo raggiungere quello che la EC2 vulnerabile avrebbe comunque potuto raggiungere, non tutto quello che è nella VPC.
Terzo livello: principle of least privilege sulle IAM role associate alle istanze. La IAM role dovrebbe avere solo i permessi strettamente necessari all'applicazione, non AdministratorAccess o policy troppo larghe. Una IAM role con permessi s3:GetObject su uno specifico bucket è significativamente meno grave se esfiltrata di una con s3:* su tutti i bucket dell'account.
Quarto livello: egress filtering. Le istanze dovrebbero poter fare outbound solo verso destinazioni esplicitamente autorizzate, non verso tutto internet. Questo richiede configurazione di NAT Gateway con route restrittive o proxy di uscita dedicato - è operativamente più complesso ma cattura SSRF che cerchino di contattare server C2 esterni dell'attaccante.
I pattern complessivi di hardening dell'infrastruttura si integrano con il framework di DevSecOps per PMI per integrare sicurezza nel ciclo di sviluppo senza frenare il team che ho descritto in un articolo dedicato, dove l'automazione dei controlli di sicurezza infrastrutturale è parte del pattern DevSecOps standard.
Audit sistematico: come trovare SSRF in un codebase Laravel esistente
Per un audit sistematico di SSRF su un codebase Laravel esistente, applico un approccio a tre livelli progressivi. Primo livello: grep su chiamate HTTP outbound nel codice. Cerco tutti gli usi di Http::get, Http::post, Http::send, curl_exec, file_get_contents (con URL), Guzzle client. Ogni occorrenza va ispezionata manualmente per verificare se la URL di destinazione è controllabile dall'utente direttamente o indirettamente.
Secondo livello: analisi dei pattern applicativi sospetti. Le tre categorie di pattern che ho descritto sopra (webhook configurabili, preview URL, proxy risorse) vanno cercate specificamente nel codice - spesso sono implementate in controller o service specifici, riconoscibili dai nomi.
Terzo livello: test dinamico con strumenti di fuzzing. Tool come BurpSuite Professional con estensione Collaborator o PortSwigger SSRF Attack Surface Analyzer testano automaticamente ogni parametro di input dell'applicazione con payload SSRF e rilevano risposte anomale. Questo è l'approccio più affidabile ma richiede tool commerciale - tipicamente lo uso come step finale nei miei assessment.
Il risultato finale dell'intervento sul cliente milanese, al termine della remediation e della verifica indipendente, è stato il seguente. Vulnerabilità SSRF iniziale risolta con proof-of-concept ri-testato e confermato non sfruttabile. Altri 4 punti di potenziale SSRF identificati nel codice durante l'audit post-remediation, tutti risolti usando il ExternalUrlValidator centralizzato. IMDSv2 obbligatorio configurato su tutte le istanze EC2. Segmentazione VPC rivista con security group più restrittivi, riducendo il perimetro di visibilità di ciascuna istanza applicativa. IAM role dell'applicazione ridotte al principio del least privilege, eliminando permessi eccessivi che erano stati assegnati in fase di bootstrap iniziale. Report di security assessment consegnato al CTO e incluso nella documentazione di due diligence per il round serie A. Round chiuso positivamente a settembre 2025. Costo consulenziale dell'intervento: 11.600 euro. Valore del round preservato dall'intervento: 4,2 milioni di euro.
Se gestisci un'applicazione PHP Laravel in infrastruttura cloud con funzionalità che effettuano chiamate HTTP verso URL potenzialmente influenzabili dall'utente - webhook, link preview, proxy di risorse, integrazioni configurabili - la probabilità statistica che esistano SSRF sfruttabili nel tuo codebase è significativa se non hai mai fatto assessment specifico su questa classe di vulnerabilità. L'impatto di una SSRF in ambiente cloud supera praticamente qualunque altra vulnerabilità applicativa comune, e la probabilità di scoperta durante una due diligence tecnica o un assessment commissionato da un cliente enterprise è alta. Se vuoi confrontarti sul tuo caso specifico con un assessment SSRF mirato, contattami per una consulenza iniziale: in due-tre giornate di penetration test focalizzato sulla classe SSRF identifico tutti i punti della tua applicazione potenzialmente vulnerabili, produco proof-of-concept dimostrativi per quelli realmente sfruttabili, e guido il team nella remediation strutturale con il pattern di URL Validator riutilizzabile calibrato sul tuo stack specifico.