🌐 Dieser Artikel ist auch verfügbar auf: English

SSRF-Grundlagen: Server-Side Request Forgery erklärt (2026)

Schematische Darstellung eines SSRF-Angriffs: Ein Angreifer bringt eine Webanwendung dazu, eine Anfrage an den internen Cloud-Metadata-Endpunkt 169.254.169.254 zu stellen.

Server-Side Request Forgery, kurz SSRF, ist eine der unterschätztesten Schwachstellen in modernen Webanwendungen. Der Kern des Problems ist verblüffend einfach: Eine Anwendung ruft im Auftrag des Nutzers eine URL ab, etwa um ein Vorschaubild zu laden oder einen Webhook auszulösen. Wenn der Server dabei nicht prüft, wohin die Anfrage tatsächlich geht, kann ein Angreifer diese Funktion umlenken. Die Anfrage geht dann nicht mehr an das erwartete Ziel, sondern an interne Dienste, an lokale Dateien oder an den Cloud-Metadata-Endpunkt, der temporäre Zugangsschlüssel ausliefert.

Das Gefährliche an SSRF ist die geliehene Vertrauensstellung: Die Anfrage kommt vom Server selbst und damit von innerhalb des Netzwerks. Firewalls, private Subnetze und interne Dienste, die niemand aus dem Internet erreichen sollte, sind für den Server oft vollständig offen. Ein Angreifer, der SSRF ausnutzt, borgt sich genau diese Innenperspektive.

Dieser Artikel erklärt SSRF von Grund auf: die Angriffstypen (In-Band, Blind, Semi-Blind), die konkreten Angriffsziele mit Payloads, die gängigen Filter-Bypässe wörtlich, und vor allem TOCTOU-sicheren Verteidigungscode für Node.js, Python und Java, den Sie direkt übernehmen können. Außerdem ordnen wir SSRF editionsscharf in die OWASP Top 10 ein, grenzen es sauber vom oft verwechselten CSRF ab und sehen uns reale Fälle wie Capital One an, mit der nötigen fachlichen Sorgfalt.

Was ist Server-Side Request Forgery?

Die OWASP Foundation definiert SSRF als einen Angriff, bei dem der Angreifer serverseitige Funktionalität missbraucht, um Anfragen an unbeabsichtigte Ziele auszulösen. Formal ist die Schwachstelle bei MITRE als CWE-918 (Server-Side Request Forgery) klassifiziert. Die Kern-Definition lautet dort: Ein Webserver empfängt eine URL oder eine ähnliche Referenz von einer vorgelagerten Komponente (typischerweise einer Nutzereingabe) und ruft den Inhalt dieser URL ab, ohne hinreichend sicherzustellen, dass die Anfrage an das erwartete Ziel geht.

Konzeptionell ist SSRF eine Spielart des sogenannten Confused Deputy. CWE-918 ist deshalb ein Kind von CWE-441 (Unintended Proxy or Intermediary) und von CWE-610 (Externally Controlled Reference to a Resource in Another Sphere). Übersetzt heißt das: Der Server wird zum verwirrten Stellvertreter, der seine eigenen Privilegien im Auftrag eines Fremden einsetzt, ohne es zu merken.

SSRF entsteht überall dort, wo eine Anwendung eine vom Nutzer beeinflusste URL abruft. Typische Auslöser sind:

  • Bild- oder Datei-Downloads von einer angegebenen URL (Vorschaubilder, Avatare, Import-Funktionen).
  • Benutzerdefinierte Webhooks, bei denen der Nutzer eine Callback-URL vorgibt.
  • Inter-Service-Kommunikation in Microservice-Architekturen.
  • URL-Preview-Funktionen (Link-Vorschauen in Chats und Kommentaren).
  • Dokument- oder PDF-Generatoren, die eingebettete Ressourcen nachladen.

Ein minimales verwundbares Beispiel

Die MITRE-CWE-918-Seite zeigt das Grundmuster in nur drei Zeilen PHP. Der Code nimmt eine URL entgegen und ruft sie ungeprüft ab:

$url = $_GET['url'];
$response = file_get_contents($url);
echo $response;

Solange der Nutzer hier https://example.com/bild.png übergibt, funktioniert alles wie gedacht. Übergibt er aber http://169.254.169.254/latest/meta-data/, ruft der Server den Cloud-Metadata-Dienst ab und gibt dessen Antwort zurück. Genau diese fehlende Zielprüfung ist die gesamte Schwachstelle.

Die drei Typen von SSRF

Für die praktische Analyse ist entscheidend, ob und wie der Angreifer die Antwort der erzwungenen Anfrage zu sehen bekommt. Daraus ergeben sich drei Typen.

In-Band-SSRF (Basic SSRF)

Beim klassischen In-Band-SSRF wird die Antwort der internen Anfrage direkt an den Angreifer zurückgegeben. Übergibt er eine URL auf ein internes Admin-Panel, sieht er dessen HTML in der Serverantwort. Das ist die für den Angreifer komfortabelste und für den Verteidiger sichtbarste Variante.

Blind SSRF

Bei Blind SSRF kann der Angreifer die Anfrage zwar auslösen, bekommt die Antwort aber nicht zurück. Der Nachweis und die Ausnutzung erfolgen dann über Seiteneffekte: über Out-of-Band-Interaktionen wie einen DNS-Lookup oder eine HTTP-Anfrage an eine vom Angreifer kontrollierte Domain. Registriert diese Domain einen eingehenden Lookup, ist bewiesen, dass der Server die Anfrage tatsächlich abgesetzt hat. Für die praktische Erkennung nutzen Tester Kollaborator-Dienste, die genau solche eingehenden Interaktionen protokollieren.

Semi-Blind SSRF

Der Zwischenfall: Der Angreifer sieht die Antwort nicht im Volltext, aber indirekte Signale wie Statuscodes, Antwortlängen oder Antwortzeiten. Aus diesen Seitenkanälen lässt sich Information ableiten, etwa ob ein interner Port offen ist (schnelle Antwort) oder geschlossen (Timeout). Dieses Muster ist die Grundlage des internen Port-Scannings über SSRF, das mitunter unter dem Fachbegriff XSPA (Cross-Site Port Attack) läuft.

Angriffsziele mit konkreten Payloads

SSRF ist kein Selbstzweck. Die Gefahr ergibt sich aus dem, was der Angreifer mit der geliehenen Innenperspektive erreicht. Die folgenden Payloads zeigen die klassischen Zielkategorien am Beispiel eines verwundbaren Parameters.

Angriff gegen den Server selbst

Über die Loopback-Adresse zwingt der Angreifer den Server, mit sich selbst zu sprechen. So werden interne Endpunkte erreichbar, die extern gar nicht geroutet werden:

POST /product/stock HTTP/1.0
Content-Type: application/x-www-form-urlencoded

stockApi=http://localhost/admin

Ein Admin-Bereich, der nur intern erreichbar ist und deshalb oft ohne zusätzliche Authentifizierung auskommt, wird auf diese Weise über den öffentlichen Endpunkt zugänglich.

Angriff gegen Backend-Systeme

Genauso lassen sich interne, nicht öffentlich routbare Systeme im privaten Subnetz ansprechen, die im blinden Vertrauen betrieben werden, ohnehin nur intern erreichbar zu sein:

POST /product/stock HTTP/1.0
Content-Type: application/x-www-form-urlencoded

stockApi=http://192.168.0.68/admin

Cloud-Metadata: das lohnendste Ziel

In der Cloud liegt unter der Link-Local-Adresse 169.254.169.254 der Instance-Metadata-Service. Er liefert Konfigurationsdaten und, kritisch, temporäre Zugangsschlüssel der IAM-Rolle aus, mit der die Instanz läuft. Bei AWS in der ersten Version (IMDSv1) genügt ein einfacher GET-Request, um die Credentials abzugreifen:

GET /user/image?imgUrl=http://169.254.169.254/latest/meta-data/iam/security-credentials/Admin-Role

Wer diese temporären Schlüssel erbeutet, kann sich damit gegenüber der Cloud-API als die Instanz ausgeben und, je nach Rechten der Rolle, auf Speicher-Buckets, Datenbanken und weitere Dienste zugreifen. Genau dieser Mechanismus macht SSRF in Cloud-Umgebungen so brisant. Wie AWS mit IMDSv2 gegensteuert, sehen Sie weiter unten im Schutzteil. GCP und Azure haben eigene Metadata-Endpunkte, die sich bewusst nicht mit einem simplen GET abrufen lassen (dazu ebenfalls unten mehr).

Lokale Dateien und interne Dienste

Über das file://-Schema lassen sich lokale Dateien auslesen, sofern der HTTP-Client der Anwendung dieses Schema unterstützt, eine Klasse eingaberelevanter Schwachstellen, die es sich mit Local File Inclusion und RFI teilt:

url=file:///etc/passwd

Interne Dienste ohne Authentifizierung sind ebenfalls beliebte Ziele. Dazu zählen Datenbanken mit HTTP-Schnittstelle (etwa MongoDB unter http://localhost:28017/) sowie In-Memory-Stores wie Redis, der über das gopher://-Schema sogar zu Remote Code Execution eskaliert werden kann, wenn der Angreifer rohe Protokollbefehle einschmuggelt. Für die reine Erkundung reicht schon eine Handvoll Standardziele:

http://localhost:80
http://127.0.0.1:80
http://0.0.0.0:80

Filter-Bypässe: warum naive Schutzmaßnahmen scheitern

Viele Anwendungen versuchen, SSRF mit einer Blocklist zu verhindern, indem sie Strings wie 127.0.0.1 oder localhost sperren. Genau davor warnt OWASP ausdrücklich: SSRF darf nicht über eine Deny-List oder reguläre Ausdrücke abgewehrt werden. Der Grund sind die zahlreichen, gleichwertigen Schreibweisen derselben Adresse.

Alternative IP-Notationen

Die Loopback-Adresse 127.0.0.1 lässt sich auf viele Arten schreiben, die derselben Zahl entsprechen und von URL-Parsern akzeptiert werden:

http://2130706433/       # Dezimal
http://0177.0.0.1/       # Oktal
http://0x7f000001        # Hexadezimal
http://127.127.127.127   # jede Adresse in 127.0.0.0/8 ist Loopback
http://127.1             # verkürzte Notation

Die Dezimalvariante ist mathematisch nachvollziehbar: 127 mal 256 hoch 3 plus 1 ergibt 2130706433. Dass gerade die oktale und hexadezimale Schreibweise real ausgenutzt wurde, belegt CVE-2016-4029: In WordPress vor 4.5 berücksichtigte die Prüfung, ob eine Adresse intern ist, die oktale und hexadezimale IP-Notation nicht, sodass sich der SSRF-Schutz über eine entsprechend präparierte Adresse umgehen ließ (nvd.nist.gov/vuln/detail/CVE-2016-4029).

IPv6-Tricks und Rebinding-Domains

Auch über IPv6 und über Wildcard-DNS-Dienste lässt sich eine Blocklist umgehen:

http://[::]:80/               # unspezifizierte Adresse
http://[::ffff:127.0.0.1]     # IPv4-mapped IPv6
localtest.me                  # löst auf 127.0.0.1 auf
company.127.0.0.1.nip.io      # nip.io gibt die eingebettete IP zurück

DNS-Rebinding und das TOCTOU-Problem

Der raffinierteste Bypass ist DNS-Rebinding. Die Idee: Der Angreifer registriert eine Domain, die beim ersten Auflösen eine harmlose öffentliche IP zurückgibt (die Validierung besteht), beim zweiten Auflösen kurz darauf aber 127.0.0.1 oder 169.254.169.254. Prüft die Anwendung die URL, löst dann den Namen erneut auf und verbindet sich mit dem zweiten Ergebnis, greift sie am Schutz vorbei ins Interne.

Das ist ein klassisches Time-of-Check-to-Time-of-Use-Problem (TOCTOU): Zwischen dem Zeitpunkt der Prüfung und dem Zeitpunkt der Nutzung ändert sich die Realität. Genau hier scheitern viele gutgemeinte Codebeispiele, die nur den Hostnamen gegen eine Allowlist prüfen und die Verbindung anschließend dem HTTP-Client überlassen, der frisch (und ungeprüft) neu auflöst. Die Lösung, die wir im Schutzteil zeigen, lautet: einmal auflösen, die IP prüfen und dann mit genau dieser geprüften IP verbinden.

Redirect-Bypass: die Brücke zum Open Redirect

Ein weiterer Bypass nutzt Weiterleitungen. Der Angreifer gibt eine erlaubte URL an, die die Validierung besteht. Diese URL antwortet aber mit einem 3xx-Redirect auf ein internes Ziel. Folgt der HTTP-Client der Anwendung dem Redirect automatisch, landet die Anfrage doch beim gesperrten Ziel:

url=https://harmlose-seite.example/redirect

Besonders gefährlich wird das, wenn die Zielanwendung selbst eine Open-Redirect-Schwachstelle enthält: Dann validiert der SSRF-Filter eine augenscheinlich eigene, vertrauenswürdige Domain, die den Angreifer per Weiterleitung trotzdem ins Interne führt. Ein Open Redirect ist damit ein Verstärker für SSRF, deshalb gehören beide Schwachstellenklassen zusammen betrachtet.

Whitelist-Bypässe über URL-Parsing

Selbst Allowlists auf Domainebene sind angreifbar, wenn die Anwendung die URL naiv zerlegt. Klassische Parser-Tricks:

https://erwarteter-host:passwort@boeser-host   # Userinfo-Teil täuscht den Host vor
https://boeser-host#erwarteter-host             # Fragment täuscht Substring-Prüfungen
https://erwarteter-host.boeser-host             # Subdomain-Trick, echter Host ist boeser-host

Diese Diskrepanzen entstehen, weil unterschiedliche URL-Bibliotheken denselben String verschieden interpretieren. Ein Substring-Check auf erwarteter-host sagt nichts darüber aus, mit welchem Host sich der Client am Ende wirklich verbindet.

SSRF wirksam verhindern

Aus den Bypässen folgt die zentrale Erkenntnis: Reine Blocklisten und Regex-Prüfungen reichen nicht. Wirksamer Schutz baut auf mehreren Prinzipien auf, die OWASP und MDN übereinstimmend nennen.

Die Grundprinzipien

  1. Allowlist statt Deny-List. Wenn die erlaubten Ziele bekannt und endlich sind, prüfen Sie strikt gegen eine positive Liste von Schema, Port und Ziel. Deny-Listen sind umgehungsanfällig.
  2. Keine vollständigen URLs vom Nutzer entgegennehmen. OWASP formuliert es klar: URLs sind schwer zuverlässig zu validieren. Die radikalste und sicherste Form ist, dem Nutzer gar keine freie URL-Eingabe zu erlauben, sondern nur eine Auswahl aus vordefinierten Zielen.
  3. URL-Schemata beschränken. Erlauben Sie ausschließlich http und https. Sperren Sie file://, gopher://, dict:// und andere Schemata konsequent.
  4. Redirects nicht automatisch folgen. Deaktivieren Sie das automatische Folgen von Weiterleitungen oder validieren Sie jedes Redirect-Ziel erneut.
  5. Least Privilege und Netzwerksegmentierung. Der ausgehende Dienst sollte mit minimalen Rechten laufen und per Firewall nach dem Prinzip Default-Deny nur die tatsächlich nötigen Ziele erreichen dürfen.

Als letzte Verteidigungslinie, nicht als Hauptschutz, empfiehlt das OWASP-Cheat-Sheet außerdem, mindestens die folgenden Bereiche zu blockieren:

ZielZu blockieren
AWS/GCP/Azure Metadata169.254.169.254, metadata.google.internal
Localhost127.0.0.0/8, 0.0.0.0/8, ::1/128
RFC1918 privat10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
Multicast224.0.0.0/4, ff00::/8

Der entscheidende Punkt: TOCTOU-sicher validieren

Viele Codebeispiele im Netz prüfen nur den Hostnamen einer geparsten URL gegen eine Allowlist und überlassen die eigentliche Verbindung dann dem HTTP-Client. Das ist wegen DNS-Rebinding unsicher: Der Client löst den Namen erneut auf und kann zwischen Prüfung und Verbindung ein anderes Ergebnis erhalten. Korrekt ist ein dreistufiges Vorgehen: Schema prüfen, Hostname einmal auflösen und jede zurückgegebene IP gegen die gesperrten Bereiche validieren, und dann mit genau dieser geprüften IP verbinden, ohne erneut aufzulösen. Der Original-Hostname bleibt dabei nur für den Host-Header und die TLS-Prüfung erhalten.

Node.js: TOCTOU-sicher

Das folgende Beispiel löst den Hostnamen genau einmal auf, prüft die IP und verbindet sich anschließend mit exakt dieser Adresse. Der Host-Header und die TLS-Servername-Angabe behalten den Originalnamen, damit virtuelle Hosts und Zertifikatsprüfung funktionieren:

const dns = require('dns').promises;
const net = require('net');
const http = require('http');
const https = require('https');
const { URL } = require('url');

const ERLAUBTE_PROTOKOLLE = new Set(['http:', 'https:']);

function istGesperrteIp(ip) {
  if (net.isIPv4(ip)) {
    const p = ip.split('.').map(Number);
    if (p[0] === 10) return true;                           // 10.0.0.0/8
    if (p[0] === 127) return true;                          // 127.0.0.0/8 Loopback
    if (p[0] === 0) return true;                            // 0.0.0.0/8
    if (p[0] === 169 && p[1] === 254) return true;          // 169.254.0.0/16 (IMDS!)
    if (p[0] === 172 && p[1] >= 16 && p[1] <= 31) return true; // 172.16.0.0/12
    if (p[0] === 192 && p[1] === 168) return true;          // 192.168.0.0/16
    if (p[0] >= 224) return true;                           // Multicast/reserviert
    return false;
  }
  const v6 = ip.toLowerCase();
  if (v6 === '::1' || v6 === '::') return true;
  if (v6.startsWith('fe80')) return true;                   // Link-Local
  if (v6.startsWith('fc') || v6.startsWith('fd')) return true; // Unique Local fc00::/7
  const mapped = v6.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
  if (mapped) return istGesperrteIp(mapped[1]);
  return false;
}

async function sichereAnfrage(userUrl) {
  const url = new URL(userUrl);

  // 1. Schema strikt einschränken
  if (!ERLAUBTE_PROTOKOLLE.has(url.protocol)) {
    throw new Error(`Protokoll nicht erlaubt: ${url.protocol}`);
  }

  // 2. Hostname EINMAL auflösen und die IP prüfen
  const { address } = await dns.lookup(url.hostname);
  if (istGesperrteIp(address)) {
    throw new Error(`Ziel-IP nicht erlaubt: ${address}`);
  }

  // 3. Mit GENAU der geprüften IP verbinden (Rebinding-sicher)
  const client = url.protocol === 'https:' ? https : http;
  const options = {
    host: address,                 // an die validierte IP binden
    servername: url.hostname,      // korrekte TLS-SNI
    port: url.port || (url.protocol === 'https:' ? 443 : 80),
    path: url.pathname + url.search,
    headers: { Host: url.hostname },
  };

  return new Promise((resolve, reject) => {
    const req = client.get(options, (res) => {
      // 4. Redirects nicht automatisch folgen
      if (res.statusCode >= 300 && res.statusCode < 400) {
        res.destroy();
        return reject(new Error(`Redirect auf ${res.headers.location} blockiert`));
      }
      let body = '';
      res.on('data', (c) => (body += c));
      res.on('end', () => resolve(body));
    });
    req.on('error', reject);
  });
}

Ein Hinweis zur Einordnung: Das native http/https-Modul von Node folgt Redirects ohnehin nicht automatisch, was hier ein sicherer Standard ist. Setzen Sie höhere Clients wie axios oder got ein, müssen Sie das Folgen von Weiterleitungen explizit abschalten.

Python (requests): TOCTOU-sicher

In Python lösen wir den Hostnamen mit socket.getaddrinfo auf, prüfen jede zurückgegebene IP mit dem ipaddress-Modul und pinnen die Verbindung über einen eigenen Adapter auf die geprüfte IP. Der server_hostname sorgt dafür, dass die TLS-Zertifikatsprüfung weiterhin gegen den Originalnamen läuft:

import ipaddress
import socket
from urllib.parse import urlparse

import requests
from requests.adapters import HTTPAdapter
from urllib3.poolmanager import PoolManager

ALLOWED_SCHEMES = {"http", "https"}


def ist_gesperrte_ip(ip_str: str) -> bool:
    ip = ipaddress.ip_address(ip_str)
    return (
        ip.is_private
        or ip.is_loopback
        or ip.is_link_local      # 169.254.0.0/16, enthält den Metadata-Endpunkt
        or ip.is_multicast
        or ip.is_reserved
        or ip.is_unspecified
    )


class GepinnteIPAdapter(HTTPAdapter):
    """Erzwingt TLS-SNI und Zertifikatsprüfung gegen den Originalnamen."""

    def __init__(self, hostname: str, **kwargs):
        self._hostname = hostname
        super().__init__(**kwargs)

    def init_poolmanager(self, connections, maxsize, block=False, **kw):
        self.poolmanager = PoolManager(
            num_pools=connections, maxsize=maxsize, block=block,
            server_hostname=self._hostname, **kw
        )


def sichere_anfrage(user_url: str, timeout: int = 5) -> requests.Response:
    parsed = urlparse(user_url)

    # 1. Schema strikt einschränken
    if parsed.scheme not in ALLOWED_SCHEMES:
        raise ValueError(f"Schema nicht erlaubt: {parsed.scheme!r}")
    hostname = parsed.hostname
    if not hostname:
        raise ValueError("Kein Hostname in der URL")

    # 2. Einmal auflösen und JEDE zurückgegebene IP prüfen
    infos = socket.getaddrinfo(hostname, parsed.port or 0, proto=socket.IPPROTO_TCP)
    ips = {info[4][0] for info in infos}
    for ip in ips:
        if ist_gesperrte_ip(ip):
            raise ValueError(f"Ziel-IP nicht erlaubt: {ip}")

    # 3. Mit genau der geprüften IP verbinden, Host-Header bleibt der Originalname
    pinned_ip = next(iter(ips))
    port = parsed.port or (443 if parsed.scheme == "https" else 80)
    pinned_url = f"{parsed.scheme}://{pinned_ip}:{port}{parsed.path or '/'}"
    if parsed.query:
        pinned_url += "?" + parsed.query

    session = requests.Session()
    session.mount(f"{parsed.scheme}://{pinned_ip}", GepinnteIPAdapter(hostname))
    return session.get(
        pinned_url, headers={"Host": hostname},
        allow_redirects=False, timeout=timeout   # 4. Redirects nicht folgen
    )

Java und Spring: TOCTOU-sicher

In Java erledigt die Klasse InetAddress einen Großteil der Bereichsprüfung. isLinkLocalAddress() erfasst dabei den kritischen Metadata-Bereich 169.254.0.0/16, isSiteLocalAddress() die privaten RFC1918-Netze. Der Validator löst den Namen auf, prüft jede Adresse und gibt die gepinnte IP für die anschließende Verbindung zurück:

import java.net.InetAddress;
import java.net.URI;
import java.util.Set;

import org.springframework.stereotype.Component;

@Component
public class SsrfGuard {

    private static final Set<String> ERLAUBTE_SCHEMES = Set.of("http", "https");

    private boolean istGesperrteAdresse(InetAddress addr) {
        return addr.isLoopbackAddress()      // 127.0.0.0/8, ::1
            || addr.isLinkLocalAddress()     // 169.254.0.0/16 (IMDS!), fe80::/10
            || addr.isSiteLocalAddress()     // 10/8, 172.16/12, 192.168/16
            || addr.isAnyLocalAddress()      // 0.0.0.0, ::
            || addr.isMulticastAddress();    // 224.0.0.0/4, ff00::/8
    }

    /** Prüft die URL und liefert die validierte, zu pinnende IP zurück. */
    public InetAddress pruefeUndPinne(String userUrl) throws Exception {
        URI uri = URI.create(userUrl);
        String scheme = uri.getScheme();
        if (scheme == null || !ERLAUBTE_SCHEMES.contains(scheme.toLowerCase())) {
            throw new SecurityException("Schema nicht erlaubt: " + scheme);
        }
        String host = uri.getHost();
        if (host == null) {
            throw new SecurityException("Kein Hostname in der URL");
        }
        InetAddress[] adressen = InetAddress.getAllByName(host);
        for (InetAddress a : adressen) {
            if (istGesperrteAdresse(a)) {
                throw new SecurityException("Ziel-IP nicht erlaubt: " + a.getHostAddress());
            }
        }
        return adressen[0]; // gepinnte, geprüfte IP
    }
}

Die eigentliche Anfrage bauen Sie dann mit einem Socket auf new InetSocketAddress(pinnedIp, port) auf und setzen den Host-Header sowie bei TLS den Original-Hostnamen als SNI, statt den Namen dem HTTP-Client erneut zur Auflösung zu überlassen. Wichtig ist auch hier, das automatische Folgen von Redirects abzuschalten.

Cloud-Metadata absichern: IMDSv2, GCP und Azure

Weil der Metadata-Endpunkt das lohnendste SSRF-Ziel in der Cloud ist, haben die Anbieter eigene Gegenmaßnahmen entwickelt. Bei AWS ist das IMDSv2.

AWS IMDSv2

Am 19. November 2019 führte AWS den Instance Metadata Service Version 2 (IMDSv2) als Defense-in-Depth-Maßnahme ein. Die Idee: Statt eines einfachen GET-Requests verlangt IMDSv2 zuerst einen PUT-Request, der ein sitzungsgebundenes Token zurückliefert. Dieses Token muss anschließend als Header bei jeder GET-Metadata-Abfrage mitgeschickt werden. Der offizielle Ablauf laut AWS:

TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`

curl http://169.254.169.254/latest/meta-data/profile \
-H "X-aws-ec2-metadata-token: $TOKEN"

Der Schutzeffekt gegen SSRF ergibt sich aus dem PUT-Zwang: Die meisten SSRF-Schwachstellen erlauben dem Angreifer nur, eine URL vorzugeben, die der Server per GET abruft. Kontrolle über die HTTP-Methode (PUT statt GET) oder über beliebige Request-Header hat er in der Regel nicht. Damit laufen klassische GET-basierte Exploits gegen die Metadaten ins Leere. Zusätzlich hat die Token-Antwort eine Standard-TTL (Hop-Limit) von 1, sodass sie nicht über die Instanz hinaus weitergeleitet werden kann, ein Schutz gegen fehlkonfigurierte Reverse-Proxys.

Wichtig zur ehrlichen Einordnung: IMDSv2 ist kein Allheilmittel. AWS selbst beschreibt es als Defense-in-Depth-Mechanismus gegen mehrere verwandte Angriffsklassen (offene WAFs, offene Reverse-Proxys, SSRF und offene Layer-3-Firewalls). Es schützt zuverlässig gegen die häufigen, einfachen GET-only-SSRF-Muster. Wer IMDSv2 erzwingt und IMDSv1 vollständig deaktiviert, schließt genau die Lücke, über die der weiter unten beschriebene Capital-One-Fall öffentlich diskutiert wurde.

GCP und Azure

Google Cloud und Azure verfolgen einen ähnlichen Ansatz: Ihre Metadata-Endpunkte lassen sich bewusst nicht mit einem simplen GET abrufen. GCP erreichen Sie unter http://metadata.google.internal/computeMetadata/v1/, aber nur mit dem Header Metadata-Flavor: Google. Azure nutzt http://169.254.169.254/metadata/instance und verlangt den Header Metadata: true sowie einen api-version-Parameter. Da einfache SSRF-Vektoren keine benutzerdefinierten Header setzen können, wirkt diese Header-Pflicht wie eine eingebaute erste Hürde, vergleichbar mit dem Token-Prinzip von IMDSv2.

SSRF in den OWASP Top 10: 2021 gegen 2025

Ein Punkt, den kaum eine andere Quelle sauber herausarbeitet, ist die editionsscharfe Geschichte von SSRF in den OWASP Top 10. Sie hat sich zwischen 2021 und 2025 grundlegend geändert.

A10:2021, SSRF als eigene Kategorie

In der Ausgabe 2021 war SSRF erstmals eine eigene Kategorie: A10:2021 Server-Side Request Forgery (SSRF). Bemerkenswert ist, wie sie dorthin kam. Sie wurde nicht aus den quantitativen Testdaten gebildet, sondern ausdrücklich aus der Community-Umfrage hinzugefügt (Platz 1 der von der Community vorgeschlagenen Ergänzungen). Mit anderen Worten: Die gemessene Häufigkeit war mit einer durchschnittlichen Incidence-Rate von 2,72 Prozent vergleichsweise niedrig, aber die Sicherheits-Community stufte das Schadenspotenzial als so hoch ein, dass SSRF trotzdem aufgenommen wurde. Der Kategorie war genau ein CWE zugeordnet: CWE-918. Insgesamt standen dahinter 385 gemeldete CVEs und 9.503 Vorkommen.

A01:2025, SSRF geht in Broken Access Control auf

In der finalen Edition 2025 (offiziell als achte Ausgabe der OWASP Top 10 bezeichnet) ist SSRF keine eigene Kategorie mehr. CWE-918 wurde in A01:2025 Broken Access Control konsolidiert und dort neben CWE-200, CWE-201 und CWE-352 ausdrücklich als eine der bemerkenswerten CWEs geführt. A01:2025 ist mit 40 gemappten CWEs die mit Abstand größte Sammelkategorie, hinter der 32.654 CVEs und über 1,8 Millionen Vorkommen stehen. SSRF ist darin nur noch eine von 40 Teilschwächen.

MerkmalA10:2021 (SSRF)A01:2025 (Broken Access Control)
Status von SSRFEigene KategorieIn A01 konsolidiert (eine von 40 CWEs)
Gemappte CWEs1 (CWE-918)40
Total CVEs38532.654
Total Occurrences9.5031.839.701
HerkunftCommunity-UmfrageDatenanalyse plus Community

Warum die Einordnung unter Broken Access Control? Sinngemäß lässt sich SSRF als eine Form fehlender Zugriffskontrolle auf Netzwerkebene verstehen: Der Server stellt Anfragen an Ressourcen, auf die er eigentlich keinen Zugriff haben sollte. Diese Deutung ist eine plausible fachliche Einordnung, kein wörtliches OWASP-Zitat. Genau deshalb ist SSRF seit 2025 nur noch ein Teilaspekt von Broken Access Control und IDOR.

Fest steht der belegbare Fakt: Wer heute nach SSRF in den OWASP Top 10 sucht, findet es unter A01, nicht mehr unter A10. Für den größeren Kontext lohnt der Blick in unseren Überblick zu den OWASP Top 10.

SSRF vs. CSRF: die gefährliche Namensverwechslung

SSRF und CSRF klingen fast gleich und werden ständig verwechselt. Technisch haben sie aber wenig gemeinsam. Die saubere Abgrenzung lässt sich an einer einzigen Frage festmachen: Wer stellt die schädliche Anfrage?

  • SSRF (Server-Side Request Forgery): Der Server stellt die Anfrage. Der Angreifer bringt die serverseitige Anwendung dazu, aus ihrer eigenen, privilegierten Netzwerkposition heraus eine Anfrage an ein internes Ziel zu senden. Es geht um den Missbrauch der Vertrauensstellung des Servers.
  • CSRF (Cross-Site Request Forgery): Der Browser des Opfers stellt die Anfrage. Der Angreifer bringt den Browser eines eingeloggten Nutzers dazu, eine zustandsändernde Anfrage samt gültigem Session-Cookie an eine Anwendung zu senden. Es geht um den Missbrauch der authentifizierten Sitzung des Nutzers.

Kurz gefasst: Bei SSRF ist der verwirrte Stellvertreter der Server, bei CSRF der Browser. SSRF zielt nach innen ins Netzwerk, CSRF zielt auf eine authentifizierte Aktion im Namen des Opfers. Eine ausführliche Behandlung des zweiten Falls finden Sie in unserem Artikel CSRF: Cross-Site Request Forgery erklärt. Verwandt ist SSRF außerdem mit XXE (XML External Entities): Ein XML-Parser, der externe Entities auflöst, kann als Einfallstor dienen, über das dann eine SSRF-artige Anfrage vom Server ausgelöst wird.

Reale Fälle: Capital One und zwei Enterprise-CVEs

Capital One 2019, der bekannteste Fall, mit einem Vorbehalt

Kein SSRF-Artikel kommt am Capital-One-Breach vorbei. 2019 verschaffte sich eine Angreiferin über eine falsch konfigurierte Open-Source-Web-Application-Firewall (ModSecurity) auf AWS Zugriff. Laut der technischen Analyse von Krebs on Security, die sich auf eine Quelle mit direkter Ermittlungskenntnis beruft, ließ sich die WAF dazu bringen, eine Anfrage an den EC2-Metadata-Service zu stellen und so die temporären IAM-Credentials der WAF-Rolle abzugreifen. Mit diesen Schlüsseln listete und exfiltrierte die Angreiferin Daten aus Capital-One-S3-Buckets.

An dieser Stelle ist fachliche Sorgfalt geboten, die den meisten Darstellungen fehlt. AWS erklärte in Korrespondenz, dass SSRF "nicht der primäre Faktor" des Angriffs gewesen sei. Die dominante SSRF-Erzählung stammt von externen Sicherheitsexperten, die die Ermittlungsakte analysiert haben, nicht von einer AWS-eigenen Bestätigung. Capital One sollte deshalb als der bekannteste öffentlich diskutierte, SSRF-assoziierte Vorfall gelten, dessen genaue Kausalkette (SSRF gegen IMDSv1 versus WAF-Fehlkonfiguration als primärer Faktor) zwischen Forschern und AWS umstritten bleibt.

Auch die Betroffenenzahl verdient Transparenz: Die Berichterstattung nennt rund 100 Millionen US-Kunden und 6 Millionen kanadische Kunden, in Summe die oft kolportierten rund 106 Millionen. Diese Summe ist zusammengerechnet, keine einzelne amtlich bestätigte Zahl. Belegt und amtlich ist dagegen die regulatorische Folge: Die US-Aufsichtsbehörde OCC verhängte am 6. August 2020 ein Bußgeld von 80 Millionen US-Dollar wegen unzureichender Risikobewertung vor der Cloud-Migration. Und passend zum Zeitstrahl führte AWS im November 2019, wenige Monate nach dem öffentlichen Bekanntwerden, IMDSv2 ein.

CVE-2021-26855 (ProxyLogon)

Dass SSRF weit mehr ist als ein Bug-Bounty-Thema, zeigt ProxyLogon. CVE-2021-26855 ist eine kritische, unauthentifizierte SSRF-Schwachstelle in Microsoft Exchange Server. Sie erlaubt einem entfernten Angreifer, beliebige HTTP-Anfragen zu senden und sich dabei als der Exchange-Server selbst zu authentifizieren. Verkettet mit CVE-2021-27065 führt sie zu vollständiger Remote Code Execution. Microsoft veröffentlichte am 2. März 2021 Sicherheitsupdates. Die Schwachstelle wurde massenhaft in freier Wildbahn ausgenutzt und steht in der CISA-Liste der Known Exploited Vulnerabilities.

CVE-2021-21973 (VMware vCenter)

CVE-2021-21973 ist eine SSRF-Schwachstelle im vSphere Client von VMware vCenter Server, verursacht durch unzureichende URL-Validierung in einem Plugin. VMware stufte sie mit einem CVSSv3-Basiswert von 5,3 selbst nur als "Moderate" ein. Trotzdem landete sie in der CISA-KEV-Liste, ein gutes Beispiel dafür, dass auch als moderat bewertete SSRF-Lücken aktiv ausgenutzt werden. Ein unauthentifizierter Angreifer mit Netzwerkzugriff auf Port 443 kann darüber interne Netzwerke erkunden und Informationen abgreifen.

Häufige Fragen

Was ist ein SSRF-Angriff?

Bei einem SSRF-Angriff (Server-Side Request Forgery) bringt ein Angreifer eine Webanwendung dazu, eine Anfrage an ein Ziel zu stellen, das er selbst bestimmt. Die Anwendung ruft eine vom Nutzer beeinflusste URL ab, ohne das tatsächliche Ziel zu prüfen. Weil die Anfrage vom Server ausgeht, borgt sich der Angreifer dessen Innenperspektive und erreicht interne Dienste, lokale Dateien oder den Cloud-Metadata-Endpunkt mit seinen Zugangsschlüsseln.

Was ist der Unterschied zwischen SSRF und CSRF?

Der entscheidende Unterschied ist, wer die schädliche Anfrage stellt. Bei SSRF stellt der Server die Anfrage aus seiner privilegierten Netzwerkposition heraus, hier geht es um den Missbrauch der Vertrauensstellung des Servers. Bei CSRF stellt der Browser des Opfers die Anfrage samt gültigem Session-Cookie, hier geht es um den Missbrauch der authentifizierten Sitzung des Nutzers. SSRF zielt nach innen ins Netzwerk, CSRF auf eine Aktion im Namen des Opfers.

Was ist Blind SSRF?

Bei Blind SSRF kann der Angreifer die Anfrage zwar auslösen, bekommt die Antwort aber nicht zurück. Der Nachweis erfolgt über Seiteneffekte, etwa über Out-of-Band-Interaktionen wie einen DNS-Lookup oder eine HTTP-Anfrage an eine vom Angreifer kontrollierte Domain. Registriert diese Domain einen eingehenden Lookup, ist bewiesen, dass der Server die Anfrage tatsächlich abgesetzt hat.

Wie kann man SSRF verhindern?

Wirksamer Schutz setzt auf eine positive Allowlist statt einer Deny-List, erlaubt nur die Schemata http und https und folgt Redirects nicht automatisch. Entscheidend ist die TOCTOU-sichere Validierung: den Hostnamen einmal auflösen, jede zurückgegebene IP prüfen und mit genau dieser geprüften IP verbinden. In der Cloud sollten Sie zusätzlich IMDSv2 erzwingen und IMDSv1 deaktivieren.

Ist SSRF noch in den OWASP Top 10 2025?

SSRF ist seit 2025 keine eigene Kategorie mehr. In der Ausgabe 2021 war es noch als A10:2021 gelistet, in der Edition 2025 wurde CWE-918 in A01:2025 Broken Access Control konsolidiert. Wer heute nach SSRF in den OWASP Top 10 sucht, findet es also unter A01, nicht mehr unter A10.

Fazit und Checkliste

SSRF entsteht immer aus derselben Wurzel: Eine Anwendung ruft eine vom Nutzer beeinflusste URL ab, ohne das tatsächliche Ziel zu prüfen. Weil die Anfrage vom Server ausgeht, borgt sich der Angreifer dessen Innenperspektive und erreicht interne Dienste, lokale Dateien und, am gefährlichsten, den Cloud-Metadata-Endpunkt mit seinen Zugangsschlüsseln. Naive Filter auf Basis von Blocklisten oder Regex scheitern an alternativen IP-Notationen, DNS-Rebinding und Redirects.

Wirksamer Schutz besteht aus wenigen, aber konsequent umgesetzten Regeln:

  • Wo möglich, dem Nutzer keine freie URL-Eingabe erlauben, sondern nur eine Auswahl vordefinierter Ziele.
  • Ansonsten eine positive Allowlist für Schema, Port und Ziel durchsetzen, keine Deny-List als Hauptschutz.
  • Nur http und https zulassen, alle anderen Schemata sperren.
  • TOCTOU-sicher validieren: einmal auflösen, jede IP prüfen, mit genau dieser IP verbinden.
  • Redirects nicht automatisch folgen.
  • In der Cloud IMDSv2 erzwingen und IMDSv1 deaktivieren, die Header-Pflicht bei GCP und Azure ergänzt das.
  • Netzwerksegmentierung und Least Privilege als Defense in Depth ergänzen, damit ein erfolgreicher Angriff möglichst wenig erreicht.

Wer diese Prinzipien beherzigt und den gezeigten TOCTOU-sicheren Code als Vorlage nutzt, macht SSRF von einer der unterschätztesten zu einer beherrschbaren Schwachstelle. Für den größeren Zusammenhang dieser Schwachstellenklassen lohnt der Blick in unseren Überblick zu den OWASP Top 10.