Skip to Content
SécuritéPartie 8 — CSRF, SSRF & CORS8.2 — SSRF côté serveur

Chapitre 8.2 — SSRF côté serveur

⏱️ TL;DR — Le SSRF (Server-Side Request Forgery) est la symétrie du CSRF : au lieu de forcer le navigateur de la victime, l’attaquant force ton serveur à émettre une requête vers une URL qu’il choisit. Dès que ton code fait un fetch/curl vers une URL fournie par l’utilisateur (webhook, import d’image par URL, aperçu de lien, proxy), l’attaquant peut le pointer vers des cibles internes inaccessibles depuis l’extérieur : services privés, bases, et surtout les métadonnées cloud (http://169.254.169.254/) qui peuvent livrer des credentials de l’infrastructure. Parade : allow-list stricte des destinations autorisées, refus des IP internes/privées, et durcissement réseau. Ne jamais requêter une URL brute fournie par l’utilisateur.

🎯 Objectifs

  • Comprendre le mécanisme du SSRF et pourquoi le serveur est une cible privilégiée.
  • Connaître les cibles typiques : services internes et métadonnées cloud.
  • Repérer les fonctionnalités à risque (URL fournie par l’utilisateur → requête serveur).
  • Appliquer la parade : allow-list de destinations, blocage des IP internes, durcissement.

Le mécanisme

Beaucoup d’applis, à un moment, font une requête sortante à partir d’une URL : importer une image depuis une adresse, générer l’aperçu d’un lien, appeler un webhook, agir en proxy, récupérer un flux. Si l’URL vient (même indirectement) de l’utilisateur, l’attaquant contrôle la destination de la requête émise par ton serveur.

// ❌ SSRF : le serveur requete une URL fournie par l'utilisateur, telle quelle $url = $_POST['image_url']; $contenu = file_get_contents($url); // et si url = http://169.254.169.254/... ?

Le danger vient de la position du serveur : contrairement au navigateur d’un attaquant (à l’extérieur), ton serveur est à l’intérieur de ton réseau. Il peut atteindre des ressources non exposées à Internet : d’autres services internes (http://localhost:6379 Redis, http://db-interne:5432), des panneaux d’admin internes, et surtout les endpoints de métadonnées cloud.

La cible reine : les métadonnées cloud

Sur la plupart des hébergeurs cloud, chaque instance peut interroger un service de métadonnées à une adresse link-local spéciale — souvent http://169.254.169.254/. Ce service fournit des informations sur l’instance… et, selon la configuration, des identifiants d’accès temporaires à l’infrastructure (rôles, clés). Un SSRF qui atteint cette adresse peut donc exfiltrer des credentials cloud et, de là, compromettre bien plus que l’appli.

# Payload SSRF classique en environnement cloud : http://169.254.169.254/latest/meta-data/... # peut livrer des credentials d'instance

C’est ce qui a transformé le SSRF, longtemps sous-estimé, en risque majeur (au point d’entrer au Top 10 OWASP). D’autres cibles : http://localhost/, http://127.0.0.1/, les plages privées (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12), et des astuces pour contourner les filtres naïfs (redirections, [::], encodages, DNS rebinding).

🎯 Côté attaquant — Face à une fonctionnalité « donne-moi une URL, je la récupère », l’attaquant essaie d’abord les cibles internes : http://169.254.169.254/ (métadonnées cloud — le graal), http://localhost:<port> (services locaux), les IP privées. Il teste aussi des contournements de filtres : une URL qui redirige vers une IP interne, un nom de domaine qui résout en IP privée (DNS rebinding), des notations alternatives (0x7f000001, [::1], 127.0.0.1.nip.io). Si le contenu récupéré lui revient (SSRF « in-band »), il lit directement les secrets ; sinon il exploite en aveugle (différences de temps/erreurs).

Repérer les fonctionnalités à risque

Toute fonctionnalité où une URL contrôlée par l’utilisateur devient une requête serveur est un vecteur SSRF potentiel :

  • Import par URL : « importer une image/un fichier depuis une adresse ».
  • Aperçu de lien (unfurling) : le serveur va chercher le titre/l’image d’une URL collée.
  • Webhooks sortants : l’utilisateur configure une URL que le serveur appellera.
  • Proxy / passerelle : le serveur relaie des requêtes vers une cible paramétrable.
  • Génération de PDF/screenshots depuis une URL, récupération de flux/XML (attention aussi aux XXE — entités externes XML — un cousin qui peut mener au SSRF).

La parade : allow-list de destinations

Le principe, cohérent avec tout le cours : ne pas faire confiance à l’URL. On n’autorise pas « toutes les URL sauf les mauvaises » (deny-list, contournable) mais uniquement un ensemble de destinations explicitement permises (allow-list).

Mesures, en couches :

  1. Allow-list de domaines/hôtes : n’autoriser les requêtes sortantes que vers une liste connue (ex. le domaine d’un partenaire précis). Tout le reste est refusé.
  2. Bloquer les adresses internes/privées : après résolution DNS, rejeter toute IP link-local (169.254.0.0/16), loopback (127.0.0.0/8, ::1), privée (10/8, 192.168/16, 172.16/12) et métadonnées. Résoudre soi-même et vérifier l’IP finale (gérer les redirections).
  3. Restreindre le schéma et le port : n’autoriser que https (voire http) vers les ports attendus ; refuser file://, gopher://, dict:// et autres schémas exotiques exploitables.
  4. Ne pas suivre les redirections aveuglément (ou revalider chaque saut).
  5. Durcissement réseau (defense in depth, lien Serveur Linux) : segmenter le réseau pour que le serveur applicatif ne puisse pas atteindre les métadonnées ou les services sensibles ; sur le cloud, exiger la version durcie du service de métadonnées quand elle existe.
// ✅ Idee : valider l'URL contre une allow-list ET verifier l'IP resolue function urlSortanteAutorisee(string $url): bool { $parts = parse_url($url); if (($parts['scheme'] ?? '') !== 'https') return false; // schema $host = $parts['host'] ?? ''; if (!in_array($host, ['cdn.partenaire.com'], true)) return false; // allow-list d'hotes $ip = gethostbyname($host); if (ipEstPriveeOuInterne($ip)) return false; // pas d'IP interne return true; }

⚠️ Piège — Filtrer par deny-list (« interdire 169.254.169.254 et localhost ») est insuffisant : l’attaquant contourne avec des notations alternatives (0x, décimal, [::1], sous-domaines qui résolvent en IP privée), des redirections, du DNS rebinding (le domaine résout en IP publique à la validation, puis en IP interne à la requête). La défense robuste est l’allow-list de destinations plus la vérification de l’IP réellement contactée (résoudre soi-même, rejeter tout ce qui est interne, revalider après redirection) — pas une liste de « mauvaises » chaînes.

💡 Réflexe — Devant tout code qui fait une requête sortante à partir d’une URL venant de l’utilisateur, l’alarme SSRF doit sonner. Demande-toi : « cette URL peut-elle pointer vers mon réseau interne ou les métadonnées cloud ? ». Si oui (et par défaut, oui), impose une allow-list et bloque les IP internes. Le motif dangereux est fetch(urlUtilisateur) / file_get_contents(urlUtilisateur) sans validation de destination.

🧭 Sur FormaCampus — FormaCampus a une fonctionnalité « importer une ressource depuis une URL » (un formateur ajoute une image/vidéo externe à son cours) et des webhooks (renvoi de notes vers un LMS partenaire — lien Interop EdTech). Les deux sont des vecteurs SSRF, traités ainsi : allow-list de domaines partenaires pour les webhooks ; pour l’import, validation stricte (schéma https, résolution DNS et rejet de toute IP interne/privée/link-local, pas de suivi aveugle des redirections). Au niveau infra, le service applicatif est segmenté pour ne pas pouvoir joindre l’endpoint de métadonnées cloud ni les bases internes directement. Ainsi, une URL pointée vers 169.254.169.254 ou localhost est refusée en amont.

✏️ Exercices

Exercice 1 — Repère le SSRF. Ce endpoint génère l’aperçu d’un lien. Où est le risque, et quelles cibles un attaquant viserait-il ?

app.post('/preview', async (req, res) => { const r = await fetch(req.body.url) // URL fournie par l'utilisateur const html = await r.text() res.json({ title: extractTitle(html) }) })

✅ Solution

Le serveur fait un fetch vers une URL fournie par l’utilisateur (req.body.url), sans aucune validation de destination → SSRF. Un attaquant viserait : http://169.254.169.254/... (métadonnées cloud, pour exfiltrer des credentials d’instance — le plus grave), http://localhost:<port> et les IP privées (services internes, bases, panneaux d’admin non exposés). Comme le titre extrait revient dans la réponse, c’est un SSRF « in-band » potentiellement très bavard. Parade : valider req.body.url contre une allow-list (schéma https, hôtes autorisés), résoudre le DNS et rejeter toute IP interne/privée/link-local, ne pas suivre aveuglément les redirections, et segmenter le réseau pour que le serveur ne puisse pas joindre les métadonnées.

Exercice 2 — Deny-list contournée. Un dev protège un import d’URL en rejetant les URL contenant 169.254.169.254 ou localhost. Donne deux façons de contourner ce filtre et la vraie parade.

✅ Solution

Contournements : (1) notations alternatives de l’adresse — http://0x7f000001/ ou http://2130706433/ (127.0.0.1 en hexa/décimal), http://[::1]/, http://127.0.0.1.nip.io/ — qui ne contiennent pas les chaînes filtrées mais pointent en interne ; (2) redirection / DNS rebinding — une URL sur un domaine de l’attaquant qui redirige vers 169.254.169.254, ou qui résout en IP publique lors de la validation puis en IP interne au moment de la requête. La deny-list de chaînes est donc vaine. Vraie parade : allow-list de destinations autorisées et vérification de l’IP réellement contactée — résoudre le DNS soi-même, rejeter toute IP interne/privée/link-local, revalider après chaque redirection (ou ne pas les suivre), restreindre schéma/port. On valide la destination réelle, pas des sous-chaînes.

🧠 Quiz de révision

1. Qu’est-ce que le SSRF, et en quoi diffère-t-il du CSRF ?

Le SSRF force ton serveur à émettre une requête vers une URL choisie par l’attaquant (via une URL fournie par l’utilisateur). Le CSRF force le navigateur de la victime. Le SSRF est dangereux car le serveur est à l’intérieur du réseau et peut atteindre des ressources non exposées.

2. Quelle est la cible « reine » d’un SSRF en environnement cloud ?

Le service de métadonnées cloud (souvent http://169.254.169.254/), qui peut livrer des identifiants d’accès temporaires à l’infrastructure. Un SSRF qui l’atteint peut exfiltrer des credentials et compromettre bien plus que l’appli.

3. Cite trois fonctionnalités typiquement vulnérables au SSRF.

Par ex. : import par URL (image/fichier), aperçu de lien (unfurling), webhooks sortants, proxy/passerelle, génération de PDF/screenshots depuis une URL, récupération de flux/XML. Tout endroit où une URL utilisateur devient une requête serveur.

4. Pourquoi une deny-list ne suffit-elle pas contre le SSRF ?

Parce qu’elle se contourne : notations alternatives d’IP (0x, décimal, [::1]), sous-domaines résolvant en IP privée, redirections, DNS rebinding. Filtrer des chaînes « interdites » laisse toujours des voies. Il faut valider la destination réelle (allow-list + IP effectivement contactée).

5. Quelle est la parade robuste au SSRF ?

Une allow-list de destinations autorisées, plus le blocage des IP internes/privées/link-local (en résolvant soi-même et en vérifiant l’IP finale, redirections comprises), la restriction du schéma/port, et le durcissement réseau (segmentation pour que le serveur ne puisse pas joindre les métadonnées/services sensibles).


Chapitre suivant : Clickjacking & open redirect — deux attaques qui abusent l’affichage et la navigation : l’iframe piège et la redirection détournée.

Last updated on