Während die meisten Web-Sicherheits-Themen auf der Anwendungs-Schicht spielen, gibt es eine eigene Klasse von Angriffen, die unter der App auf der HTTP-Protokoll-Ebene stattfinden — zwischen Reverse-Proxy, Load Balancer und Backend-Server. HTTP Request Smuggling und Host-Header-Injection sind die zwei wichtigsten Vertreter. Dieser Artikel ordnet beide Klassen: was sie technisch sind, wie sie reale Angriffe (Cache-Poisoning, Auth-Bypass, Password-Reset-Hijacking) ermöglichen — und welche Härtungs-Patterns sie verhindern.

HTTP Request Smuggling: das Grund-Prinzip

In modernen Web-Architekturen liegt fast immer ein Front-End-Server vor dem Backend — eine Cloudflare, ein Nginx, ein AWS-Load-Balancer, ein ALB. Front-End empfängt HTTP-Anfragen vom Internet und reicht sie an das Backend weiter.

Das funktioniert, solange Front-End und Backend dieselbe HTTP-Interpretation haben. Wenn nicht — wenn beide eine HTTP-Anfrage anders parsen — entsteht Request Smuggling: eine vom Front-End als „eine Anfrage" gesehene Bytes-Folge wird vom Backend als „zwei Anfragen" interpretiert. Die zweite Anfrage ist dem Front-End versteckt und nicht durch dessen Sicherheits-Filter gelaufen.

Die Diskrepanz entsteht meist an Längen-Angaben der HTTP-Anfrage. HTTP/1.1 erlaubt zwei Mechanismen:

  • Content-Length: N — der Body ist genau N Bytes lang.
  • Transfer-Encoding: chunked — der Body ist in Chunks mit jeweils eigener Längen-Angabe.

Eine Anfrage soll laut Standard nur einen dieser beiden Mechanismen nutzen. Wenn beide gesetzt sind — und Front-End/Backend wählen unterschiedliche —, entsteht Smuggling.

Die Smuggling-Varianten

Drei klassische Varianten (Klassifikation von James Kettle / PortSwigger):

CL.TE — Front-End nutzt Content-Length, Backend nutzt Transfer-Encoding.

HTTP cl-te-smuggling.http
POST / HTTP/1.1
Host: example.com
Content-Length: 13
Transfer-Encoding: chunked

0

SMUGGLED
  • Front-End sieht Content-Length: 13 → liest 13 Bytes → leitet alles weiter.
  • Backend sieht Transfer-Encoding: chunked → liest bis zum 0-Chunk-End → die danach folgenden Bytes SMUGGLED sind für das Backend eine separate, geschmuggelte Anfrage.

TE.CL — umgekehrt.

Front-End versteht Transfer-Encoding, Backend versteht Content-Length. Smuggling-Payload ist anders konstruiert, der Effekt analog.

TE.TE — beide verstehen TE, eines „obfuskiert" weg.

Manche Server akzeptieren Transfer-Encoding: chunked mit kreativer Whitespace- oder Case-Variation als gültig (z. B. Transfer-Encoding : chunked mit Leerzeichen). Andere lehnen es ab. Wenn Front-End und Backend unterschiedlich tolerant sind, kommt es zu Smuggling.

HTTP/2-Downgrade-Smuggling.

Front-End spricht HTTP/2, Backend HTTP/1.1. Front-End wandelt um. Manche HTTP/2-Anfragen lassen sich so konstruieren, dass sie nach Downgrade zwei HTTP/1.1-Anfragen ergeben. Diese Klasse wurde 2021 von James Kettle ausführlich erforscht.

Was Smuggling angreift

Die geschmuggelte zweite Anfrage wird dem nächsten Front-End-Aufruf zugeschrieben. Das hat mehrere böse Konsequenzen:

  • Auth-Bypass. Die geschmuggelte Anfrage erbt die Auth-Cookies des nächsten unbeteiligten Nutzers — Angreifer bekommt Zugang zu fremder Session.
  • Cache-Poisoning. Geschmuggelte Anfrage lässt das Backend eine manipulierte Antwort zurückgeben, die im Front-End-Cache landet und an alle nachfolgenden Nutzer:innen ausgeliefert wird.
  • Information-Disclosure. Die geschmuggelte Anfrage triggert eine Backend-Antwort, die dem nächsten User-Request als Body angehängt wird.
  • Backend-Header-Injection. Geschmuggelter Inhalt kann Backend-Header setzen, die Front-End-Filter umgehen.

Reale Vorfälle: James Kettle hat seit 2019 in mehreren PortSwigger-Veröffentlichungen Hunderte realer Smuggling-Bugs bei großen Unternehmen dokumentiert (Bug-Bounty-Berichte mit fünfstelligen Auszahlungen sind nicht selten).

Smuggling verhindern

Die Verteidigung läuft auf zwei Ebenen:

1. Front-End-Backend-Konsistenz. Beide Komponenten müssen dieselbe HTTP-Interpretation haben. Idealerweise: nur eine Längen-Angabe pro Anfrage akzeptieren, alle anderen ablehnen. Front-End und Backend von demselben Hersteller oder zumindest in einer getesteten Kombination verwenden.

2. HTTP/2 End-to-End. HTTP/2 hat eine strikte binäre Frame-Struktur ohne Längen-Doppeldeutigkeiten. Wenn Front-End und Backend HTTP/2 sprechen, ist die klassische CL/TE-Smuggling-Klasse strukturell unmöglich. Aber: Downgrades innerhalb der Kette (HTTP/2 vor → HTTP/1.1 dahinter) bringen das Problem zurück.

Konkrete Patterns:

  • Reverse-Proxy normalisierenproxy_request_buffering on in Nginx, normalisierte Header.
  • Transfer-Encoding: chunked mit gleichzeitigem Content-Length strikt ablehnen (RFC 7230 verlangt das).
  • HTTP/2 mit h2c-Downgrade-Schutz auf Backend-Seite.
  • Front-End-Konfiguration: proxy_http_version 1.1 und konsistente Connection-Behandlung.

Werkzeuge zum Testen:

  • Burp Suite Pro — eingebauter Smuggling-Scanner („HTTP Request Smuggler"-Plugin von Albinowax/Kettle).
  • Smuggler.py — Open-Source-Test-Tool für CL/TE-Varianten.

Host-Header-Injection: das andere HTTP-Klassen-Angriff

Eine ganz andere HTTP-Schicht-Schwachstelle, mit ähnlichem Hebel: Anwendung nutzt den Host:-Header der HTTP-Anfrage, um interne Werte zu konstruieren — URLs in Mails, Cache-Keys, Redirects, Login-Cookies. Wenn der Host-Header durch User-Input gesetzt werden kann, lassen sich diese Werte manipulieren.

Klassisches Beispiel: Password-Reset-Hijacking.

Python password-reset-host-header.py
# Schadhaft: Reset-Link aus Host-Header gebaut
@app.post('/password-reset')
def password_reset():
  email = request.form['email']
  token = generate_token(email)
  host = request.headers['Host']  # vom Angreifer kontrolliert!
  reset_url = f"https://{host}/reset?token={token}"
  send_email(email, f"Reset link: {reset_url}")
  return 'OK'

Angreifer schickt:

HTTP password-reset-attack.http
POST /password-reset HTTP/1.1
Host: attacker.example
Content-Type: application/x-www-form-urlencoded

email=victim@example.com

Wenn der Server keinen Front-End-Host-Filter hat, baut er den Reset-Link mit Angreifer-Domain. Das Opfer bekommt eine Mail vom echten Service mit echtem Token — aber Link auf Angreifer-Server. Klick → Angreifer hat den Token → kann Passwort zurücksetzen.

Weitere Host-Header-Injection-Angriffe

Cache-Poisoning über Host-Header:

HTTP cache-poisoning.http
GET /index.html HTTP/1.1
Host: attacker.example
X-Forwarded-Host: attacker.example

Wenn die Anwendung Inhalte vom Host-Header in die Antwort einbaut (z. B. absolute URLs zu CSS/JS), und die Antwort im Cache landet, sehen alle nachfolgenden Besucher:innen die manipulierten URLs — und laden CSS/JS vom Angreifer.

Routing-Bypass: Manche Multi-Tenant-Setups routen basierend auf Host-Header. Manipulation kann zu unbeabsichtigten Tenant-Switches führen.

Login-Cookie-Domain-Manipulation: Wenn das Cookie auf Set-Cookie: ... Domain={host} gesetzt wird, kann Angreifer Cookie auf falsche Domain platzieren.

SSRF über Host-Header: Wenn die Anwendung im Backend basierend auf Host-Header weitere HTTP-Anfragen macht (z. B. interne Service-Discovery), führt Host-Header-Manipulation zu SSRF.

Host-Header-Injection verhindern

Drei Schichten:

1. Strenge Host-Whitelist. Anwendung akzeptiert nur explizit erlaubte Host-Header. Anfragen mit fremdem Host-Header werden mit 400 abgelehnt.

JavaScript host-whitelist.js
// Express-Middleware
const ALLOWED_HOSTS = new Set(['example.com', 'www.example.com', 'api.example.com']);

app.use((req, res, next) => {
  if (!ALLOWED_HOSTS.has(req.hostname)) {
    return res.status(400).send('Invalid host');
  }
  next();
});

2. Reverse-Proxy enforce-Default. Nginx, Caddy oder Traefik so konfigurieren, dass nur erlaubte Host-Header durchgelassen werden:

Nginx nginx-host-whitelist.conf
server {
  listen 443 ssl;
  server_name example.com www.example.com;
  # Kein default_server — andere Hosts laufen ins Leere
  ...
}

server {
  listen 443 ssl default_server;
  server_name _;
  return 400;  # alle anderen Host-Header ablehnen
}

3. URL-Konstruktion aus Konfiguration, nicht aus Request. Anwendung baut keine URLs aus Host:-Header. Stattdessen aus einer hartkodierten Server-Konfiguration:

Python reset-url-aus-config.py
# Sicher
BASE_URL = os.environ['APP_BASE_URL']  # z.B. "https://example.com"

@app.post('/password-reset')
def password_reset():
  email = request.form['email']
  token = generate_token(email)
  reset_url = f"{BASE_URL}/reset?token={token}"  # Host aus Config
  send_email(email, f"Reset link: {reset_url}")
  return 'OK'

Vergiss auch nicht X-Forwarded-Host, X-Forwarded-Server, X-Forwarded-Proto und ähnliche Header — sie werden von Reverse-Proxies oft an Backend weitergereicht und sind ebenso manipulierbar, wenn das Front-End sie nicht überschreibt.

Gemeinsame Lehre: HTTP-Schicht-Hygiene

Beide Angriffsklassen — Request Smuggling und Host-Header-Injection — zeigen, dass die HTTP-Protokoll-Schicht selbst eine Sicherheits-Sphäre ist. App-Code-Härtung allein reicht nicht; Reverse-Proxy-Konfiguration, Backend-HTTP-Parsing-Verhalten und Header-Hygiene sind eigenständige Themen.

Was beide Klassen verbindet:

  • Diskrepanzen zwischen Schichten sind die Angriffs-Wurzel (Front-End vs. Backend bei Smuggling; Reverse-Proxy vs. App bei Host-Header).
  • Standards sind streng, Implementierungen sind tolerant. Browsers, Proxies, Backends akzeptieren oft mehr als der Standard erlaubt — genau dort entstehen Lücken.
  • Test-Automatisierung ist schwach. Diese Angriffe brauchen manuelles Verständnis der HTTP-Schicht; Standard-Scanner finden sie nicht zuverlässig.

Für Web-Sicherheits-Reife heißt das: HTTP-Schicht ist nicht nur „Infrastruktur" — sie ist Teil der Anwendungs-Sicherheit. Reverse-Proxy-Konfiguration verdient denselben Code-Review wie App-Code.

Besonderheiten

James Kettle als Forschungs-Pionier

Die meisten heute bekannten Request-Smuggling-Varianten gehen auf James Kettle bei PortSwigger zurück. Sein DEF-CON-Vortrag "HTTP Desync Attacks: Request Smuggling Reborn" (2019) hat das Thema modernisiert. Spätere Forschung 2021 zu HTTP/2-Downgrade-Smuggling und 2023 zur Single-Packet-Attack ergänzt das Bild.

HTTP/2 löst nicht alles

HTTP/2 hat die klassischen CL/TE-Klassen strukturell eliminiert — aber HTTP/2-Frame-Parsing hat eigene Schwachstellen, und der häufige Fall „HTTP/2 vorn, HTTP/1.1 hinten" bringt das Problem zurück. End-to-End-HTTP/2 ist die saubere Lösung, aber operationell oft nicht durchgesetzt.

Bug-Bounty-Welt liebt Request Smuggling

Smuggling-Bugs zahlen typisch im hohen vierstelligen bis fünfstelligen Dollar-Bereich. Reports gegen Slack, GitHub, AWS, Microsoft, Google waren in den letzten Jahren öffentlich dokumentiert. Wer Bug-Bounty-Berichte verfolgt, sollte HackerOne und PortSwigger-Blog regelmäßig lesen.

Cloudflare und WAF helfen — und schaffen Probleme

WAFs (Cloudflare, AWS WAF, Akamai) können Standard-Smuggling-Patterns blockieren. Gleichzeitig sind sie selbst ein neuer Layer mit eigener HTTP-Interpretation — neue Diskrepanzen sind möglich. Wer WAF einsetzt: das ist ein Sicherheits-Gewinn, ersetzt aber nicht Backend-Härtung.

Host-Header-Injection ist subtil

Anders als XSS oder SQLi sieht der Angriff harmlos aus — der/die Angreifer:in sendet einfach eine Anfrage mit fremdem Host:-Header. Erst die Folgen in der Anwendung (Mail-Link, Cache-Eintrag, Redirect) machen es zum Angriff. Viele Apps haben das Problem über Jahre, ohne dass es bemerkt wird.

Frameworks helfen — wenn sie richtig konfiguriert sind

Django hat ALLOWED_HOSTS als Pflicht-Setting in Production. Rails hat ähnliche Mechanismen. Wer in einem dieser Frameworks arbeitet: Setting prüfen, nicht auf Default verlassen. Express, Flask, FastAPI haben kein eingebautes Pendant — eigene Middleware nötig.

HTTP Smuggler von Albinowax

Das Burp-Plugin HTTP Request Smuggler ist die De-facto-Standard-Test-Lösung. Automatisiert die wichtigsten Smuggling-Varianten und zeigt verschiedene Eskalationen. Wer Pentests betreibt: Pflicht-Toolkit.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu OWASP Top 10

Zur Übersicht