Exploite les en-têtes HTTP de confiance côté serveur (X-Forwarded-For, Host, X-Forwarded-Host) pour rediriger les requêtes sortantes vers des destinations contrôlées par l'attaquant.
TL;DR
X-Forwarded-Host provoque une requête SSRF sortante\r\n dans la valeur d'en-tête injecte des en-têtes arbitraires dans les requêtes en avalX-Original-URL, X-Rewrite-URL, X-Forwarded-For: 127.0.0.1Le SSRF basé sur les en-têtes exploite les en-têtes HTTP contenant des valeurs de hostname ou d'URL consommées côté serveur. Au lieu d'un paramètre URL, le vecteur d'attaque est l'en-tête de requête lui-même — spécifiquement des en-têtes conçus pour la traversée de proxy et le routage qui exposent des URLs internes lorsqu'ils sont approuvés sans validation.
Deux sous-catégories distinctes existent avec des mécaniques d'exploitation et des profils d'impact différents :
Sous-catégorie A — Vrai SSRF (confirmé OOB) : la valeur d'en-tête injectée amène le serveur ou le framework à effectuer une requête HTTP vers une URL contrôlée par l'attaquant. La requête peut être confirmée via callback OOB (Burp Collaborator, hit DNS/HTTP Interactsh). CVE-2026-27739 (Angular SSR) est l'exemple canonique de 2026.
Sous-catégorie B — Contournement d'accès (confirmable différentiellement) : l'en-tête injecté trompe le serveur en lui faisant croire que la requête entrante provient de localhost ou d'une IP privilégiée, contournant les contrôles d'accès basés sur les IPs. Aucune requête sortante n'est effectuée — le serveur lui-même sert la ressource protégée. X-Forwarded-For: 127.0.0.1 est l'exemple canonique.
Les deux sous-catégories relèvent de CWE-918 et OWASP A10:2021 car toutes deux exploitent la position réseau de confiance du serveur. Le vrai SSRF est généralement plus sévère (accès arbitraire au réseau interne) ; le contournement d'accès est plus consistant (fonctionne même quand l'egress est restreint).
Le pattern Node.js vulnérable :
// VULNÉRABLE — X-Forwarded-Host contrôle le fetch côté serveur
app.get('/dashboard', async (req, res) => {
// Le framework lit l'en-tête pour le contexte SSR
const host = req.headers['x-forwarded-host'] || req.headers['host'];
// L'attaquant injecte X-Forwarded-Host: attacker.com
const config = await fetch(`http://${host}/api/config`);
return res.render('dashboard', { config: await config.json() });
});// SÛR — URL de service interne codée en dur depuis l'environnement
const INTERNAL_API_BASE = process.env.INTERNAL_API_URL; // ex. "http://api-svc:8080"
app.get('/dashboard', async (req, res) => {
const config = await fetch(`${INTERNAL_API_BASE}/api/config`);
return res.render('dashboard', { config: await config.json() });
});| En-tête | CVE | Frameworks | CVSS | Confirmation |
|---|---|---|---|---|
X-Forwarded-Host: attacker.com | CVE-2026-27739 | Angular SSR, Astro, Symfony, Django, Next.js SSR | 9.2 | Callback HTTP OOB |
Forwarded: for=127.0.0.1;host=attacker.com;proto=http | CVE-2026-32762 | Rack, Ruby on Rails | HIGH | Callback HTTP OOB |
Host: attacker.com | — | Divers proxies inverses, frameworks SSR | HIGH | Callback HTTP OOB |
X-Pingback: http://attacker.com/ | — | WordPress XMLRPC | HIGH | Callback HTTP OOB |
Referer: http://169.254.169.254/ | — | Backends analytiques, trackers de liens | MEDIUM | Réponse différentielle |
| En-tête | Valeur | Cible du contournement | Frameworks |
|---|---|---|---|
X-Original-URL | /admin/secret | Routes admin restreintes par IP | IIS/ARR, Symfony, Drupal, ASP.NET |
X-Rewrite-URL | /admin/secret | Routes admin restreintes par IP | Symfony, Drupal |
X-Forwarded-For | 127.0.0.1 | Endpoints localhost uniquement | Express trust proxy, Spring, 1Panel |
X-Custom-IP-Authorization | 127.0.0.1 | Middleware IP personnalisé | Spécifique à l'application |
X-Real-IP | 127.0.0.1 | Nginx $realip_remote_addr | Nginx upstream |
X-Client-IP | 127.0.0.1 | Divers | Apache mod_remoteip |
L'attaque de contournement d'accès :
# Cible : panel admin interne accessible uniquement depuis 127.0.0.1
# Sans contournement :
GET /admin/users HTTP/1.1
Host: target.com
# → 403 Forbidden (vérification IP : le client est 203.0.113.5, pas 127.0.0.1)
# Avec contournement X-Original-URL :
GET / HTTP/1.1
Host: target.com
X-Original-URL: /admin/users
# → 200 OK (le serveur route vers /admin/users, X-Original-URL remplace)
# Avec contournement X-Forwarded-For :
GET /admin/users HTTP/1.1
Host: target.com
X-Forwarded-For: 127.0.0.1
# → 200 OK (le framework voit l'IP distante comme 127.0.0.1, accorde l'accès admin)Quand les valeurs d'en-têtes sont reflétées dans les requêtes HTTP en aval sans sanitisation CRLF, un attaquant peut injecter des en-têtes arbitraires :
# Nom d'en-tête webhook injecté (pattern CVE-2025-6454)
Header-Name: value\r\nX-Internal-Auth: bypass\r\nHost: metadata.internal
# Résulte dans la requête en aval contenant :
Header-Name: value
X-Internal-Auth: bypass
Host: metadata.internalLa fonctionnalité d'en-têtes personnalisés de webhook GitLab (CVE-2025-6454, CVSS 8.5) injectait des en-têtes arbitraires dans les requêtes webhook sortantes en plaçant \r\n dans les champs de nom d'en-têtes. L'en-tête Host: 169.254.169.254 injecté qui en résultait amenait les déploiements basés sur des proxies à router la requête webhook vers l'endpoint IMDS.
CVE-2026-27739 — Angular SSR (CVSS 9.2) — Le moteur de rendu côté serveur d'Angular lit l'en-tête X-Forwarded-Host lors de la construction des URLs pour les fetches d'API internes. L'en-tête est consommé sans validation, amenant le framework à effectuer une requête HTTP vers http://<hôte-injecté>/api/config. C'est un vrai SSRF sortant — confirmé via callback OOB. Sévérité critique car Angular SSR est largement déployé dans les stacks d'entreprise adjacentes à Next.js, et la construction d'URL d'API interne est un pattern au niveau du framework.
CVE-2026-32762 — Rack (Ruby on Rails) (HIGH) — L'en-tête HTTP Forwarded (RFC 7239) dans les applications basées sur Rack était traité côté serveur pour construire des URLs de requêtes en amont. Le paramètre host= dans la valeur de l'en-tête Forwarded amenait l'application à effectuer des requêtes sortantes vers l'hôte injecté. Les applications Rails et Sinatra utilisant ActionDispatch::RemoteIp étaient affectées.
CVE-2025-6454 — GitLab CE/EE (CVSS 8.5, CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H) — La fonctionnalité d'en-têtes personnalisés de webhook GitLab (introduite dans 16.11) permettait l'injection CRLF dans les champs de nom d'en-têtes. Les utilisateurs Developer+ pouvaient forger des en-têtes HTTP arbitraires dans toutes les requêtes webhook sortantes. Dans les environnements basés sur des proxies, les en-têtes Host: injectés reroutaient le trafic webhook vers des services internes. Corrigé dans GitLab 18.1.6, 18.2.6, 18.3.2.
CVE-2024-40898 — Apache HTTP Server (HIGH, Windows) — Les règles mod_rewrite en contexte serveur sur Windows ne parvenaient pas à restreindre les requêtes sortantes. Les requêtes craftées forçaient le serveur à se connecter à des partages SMB arbitraires via la gestion des chemins UNC, déclenchant l'authentification NTLM automatique de Windows — faisant fuiter des hash NetNTLM vers l'attaquant pour le craquage hors ligne ou les attaques de relais. Prime : HackerOne #2123113, 4 920 $ de l'Internet Bug Bounty.
Les variantes de contournement d'accès (X-Forwarded-For, X-Original-URL) sont fréquemment testées et corrigées individuellement. Cependant, les nouvelles combinaisons d'en-têtes — particulièrement dans les frameworks SSR récents — continuent d'introduire un vrai SSRF via des pipelines en-tête-vers-requête-sortante. Tester les deux sous-catégories indépendamment.
Ajouter les en-têtes suivants à chaque requête ciblant l'application :
X-Forwarded-Host: <id-burp-collaborator>.oastify.comX-Forwarded-For: 127.0.0.1Host: <id-burp-collaborator>.oastify.com (ajouter un second en-tête Host)X-Original-URL: /adminReferer: https://<id-burp-collaborator>.oastify.com/Vérifier Burp Collaborator pour les interactions DNS/HTTP après chaque requête.
Pour les tests de contournement d'accès, ajouter X-Forwarded-For: 127.0.0.1 aux requêtes retournant 403 et observer si la réponse passe à 200. Comparer la longueur et le corps du contenu entre la requête originale et la requête injectée.
Pour l'injection CRLF, tester les valeurs d'en-têtes avec des séquences %0d%0a intégrées :
X-Custom-Header: value%0d%0aX-Injected: bypassPour le vrai SSRF basé sur les redirections, tester X-Forwarded-Host sur les pages qui utilisent le rendu côté serveur ou effectuent des appels d'API internes.
# Test vrai SSRF — vérifier Collaborator pour le callback HTTP
GET /api/dashboard HTTP/1.1
Host: target.example.com
X-Forwarded-Host: attacker.oastify.com
# Test de contournement d'accès — comparer les réponses
GET /admin/panel HTTP/1.1
Host: target.example.com
X-Forwarded-For: 127.0.0.1
# Test d'injection CRLF
POST /api/webhook/create HTTP/1.1
Content-Type: application/json
{"name": "test\r\nX-Internal: admin", "url": "https://legitimate.com"}La détection d'injection d'en-têtes de BreachVex teste les deux sous-catégories avec prévention des FP : la stabilité de référence nécessite plusieurs échantillons au même statut avant les tests ; la réflexion de réponse est traitée comme faiblement fiable (une valeur injectée renvoyée dans le corps relève d'une réflexion côté serveur probable, pas d'un fetch sortant) ; les codes de statut CDN 421, 451, 521-526 sont exclus ; un seuil minimal de delta de corps filtre le bruit. Les en-têtes de type OOB (X-Forwarded-Host) nécessitent un canal de callback hors-bande actif — sans lui, la sonde est ignorée pour éviter les faux positifs.
# VULNÉRABLE — X-Forwarded-Host contrôle le fetch sortant
def get_dashboard_config(request):
host = request.headers.get("X-Forwarded-Host") or request.headers.get("Host")
config = requests.get(f"http://{host}/api/config") # SSRF
return config.json()
# SÛR — URL de service interne codée en dur depuis l'environnement
INTERNAL_CONFIG_API = os.environ["INTERNAL_CONFIG_API_URL"] # ex. "http://config-svc:8080"
def get_dashboard_config(request):
config = requests.get(f"{INTERNAL_CONFIG_API}/api/config")
return config.json()import re
def sanitize_header_value(value: str) -> str:
"""Supprimer les séquences CRLF des valeurs d'en-têtes — prévient l'injection d'en-têtes."""
return re.sub(r'[\r\n]', '', value)
# Appliquer à toutes les valeurs d'en-têtes fournies par l'utilisateur avant transmission
def forward_webhook(webhook_headers: dict, payload: bytes) -> None:
sanitized = {
sanitize_header_value(k): sanitize_header_value(v)
for k, v in webhook_headers.items()
}
requests.post(webhook_url, data=payload, headers=sanitized)# Nginx — supprimer les en-têtes transmis depuis des sources non de confiance
# Accepter X-Forwarded-For uniquement depuis les proxies en amont connus
real_ip_header X-Forwarded-For;
set_real_ip_from 10.0.0.0/8; # plage interne de confiance uniquement
set_real_ip_from 172.16.0.0/12;
real_ip_recursive on;
# Effacer X-Forwarded-Host avant de passer au backend
proxy_set_header X-Forwarded-Host "";// Express — configurer trust proxy explicitement, ne jamais utiliser l'en-tête Host pour les fetches
app.set('trust proxy', '10.0.0.0/8'); // ne faire confiance qu'aux IPs de proxy connues
// Ne jamais utiliser req.hostname pour les requêtes sortantes — lit Host/X-Forwarded-Host
const INTERNAL_API = 'http://api.internal:8080'; // codé en durLe pattern de contournement d'accès (X-Forwarded-For: 127.0.0.1 accordant l'accès admin) est un défaut de logique côté serveur, pas un problème au niveau réseau. Le corriger nécessite de supprimer toute confiance aux en-têtes transmis pour les décisions de contrôle d'accès. Le contrôle d'accès basé sur les IPs pour les endpoints admin doit utiliser l'adresse distante réelle du socket, jamais des en-têtes que les clients peuvent contrôler.
Le SSRF basé sur les en-têtes survient quand un en-tête de requête HTTP contenant un hostname ou une URL est consommé côté serveur et provoque une requête HTTP sortante vers une destination contrôlée par l'attaquant. La variante à l'impact le plus élevé utilise X-Forwarded-Host pour contrôler l'en-tête Host dans les requêtes internes proxifiées, amenant les frameworks à récupérer depuis attacker.com au lieu du service interne prévu.
Les en-têtes principaux sont : X-Forwarded-Host (contrôle l'en-tête Host proxifié), Host (routage d'hôte virtuel), X-Forwarded-For (usurpation d'IP pour le contournement d'accès admin), X-Original-URL et X-Rewrite-URL (remplacement d'URL dans IIS/Symfony/Drupal), Referer (récupéré par les systèmes d'analyse/journalisation), et X-Pingback (URL de callback WordPress XMLRPC).
CVE-2026-27739 (CVSS 9.2) affecte Angular SSR. Le framework traite l'en-tête X-Forwarded-Host lors de la construction des URLs d'API internes pour le rendu côté serveur. Un attaquant injecte X-Forwarded-Host: attacker.com amenant Angular SSR à effectuer une requête HTTP vers http://attacker.com/api/config au lieu de l'endpoint interne légitime. Confirmé via callback OOB.
L'injection CRLF intègre des caractères carriage return (\r) et line feed (\n) dans les valeurs d'en-têtes. Quand une valeur d'en-tête est reflétée dans une requête HTTP en aval, la séquence CRLF injectée termine l'en-tête courant et insère de nouveaux en-têtes arbitraires. CVE-2025-6454 (GitLab) utilisait l'injection CRLF dans les noms d'en-têtes personnalisés de webhook pour insérer des en-têtes incluant Host: metadata.internal dans les requêtes webhook sortantes.
Le vrai SSRF d'en-têtes (sous-catégorie A) amène le serveur à effectuer une requête HTTP vers une URL contrôlée par l'attaquant, confirmé via callback OOB. Le contournement d'accès (sous-catégorie B) utilise des en-têtes comme X-Forwarded-For: 127.0.0.1 ou X-Original-URL: /admin pour tromper le serveur en lui faisant croire que la requête provient de localhost, contournant les contrôles d'accès basés sur les IPs — aucune requête sortante n'est effectuée.
De nombreux frameworks exposent des endpoints admin accessibles uniquement depuis localhost (127.0.0.1). Certains font confiance à l'en-tête X-Forwarded-For lorsqu'ils se trouvent derrière un proxy. L'injection de X-Forwarded-For: 127.0.0.1 amène le framework à traiter la requête comme provenant de localhost, accordant l'accès admin. Express trust proxy, la résolution RemoteAddr de Spring et 1Panel sont des frameworks affectés connus.
Les codes HTTP 421 (désaccord TLS/SNI), 451 (blocage géo/légal) et 521-526 (erreur backend Cloudflare) doivent être exclus de l'analyse différentielle SSRF. Les pages d'erreur Cloudflare comportent 250-500 octets de bruit qui peuvent déclencher de faux différentiels de longueur de corps. La stabilité de référence nécessite 3 échantillons avec le même code de statut avant de tester les en-têtes injectés.
Les systèmes d'analyse, l'infrastructure de journalisation et les plateformes de suivi de liens récupèrent l'URL Referer côté serveur pour catégoriser les sources de référence ou capturer des captures d'écran. Un attaquant définit Referer: http://169.254.169.254/latest/meta-data/ dans une requête vers une page suivie par un tel système. Le backend analytique récupère l'URL Referer depuis son contexte serveur, atteignant le service de métadonnées.