~/blog/cors-misconfiguration-single-findingPOSTED
von Shady Nathan Tawfik · 1. August 2026 · 4 min

Ein Befund, maximale Wirkung: eine zu permissive Cross-Origin-Policy

Eine kommunale Content-Plattform auf modernem Stack ergab einen einzigen Befund höchster Schwere. Er brauchte keine Versionsprüfung und keinen Exploit; der Browser hat die Arbeit erledigt.

Interaktiv[blog-2.0]

TL;DR: Ein Befund, maximale Wirkung: eine Wildcard-Cross-Origin-Policy auf der öffentlichen Datenschnittstelle. Keine Versionsprüfung, kein Exploit — der Browser erledigt die Arbeit. Ich zeige den exakten Request und den Allowlist-Fix.

Ausgangslage

Ich habe das Ziel als Black-Box geprüft: keine Zugangsdaten, nur die öffentlich sichtbare Fläche. Das ist, was ich sah, bevor ich etwas angefasst habe.

Eine kommunale Content-Plattform auf einem unterstützten Content-System hinter einem modernen Reverse Proxy. Die Bewertung war bewusst eng gefasst und prüfte die öffentliche Datenschnittstelle sowie die Cross-Origin-Policy für den Browserzugriff darauf.

Ich habe alle identifizierenden Informationen entfernt. Das Ziel wird ausschließlich nach Branche und Technologieklasse beschrieben, damit das Muster übertragbar bleibt.

Das Muster

Das Muster, das ich erkannt habe — und das ich in ähnlichen Landschaften immer wieder finde:

Cross-Origin-Policy ist ein Browser-Mechanismus, und genau das macht sie mächtig und billig. Antwortet ein Server auf eine Cross-Origin-Anfrage mit permissiver Policy, setzt der Browser sie durch: Die aufrufende Seite darf die Antwort lesen. Der Server hat aus seiner Sicht nichts falsch gemacht, keine Version ist veraltet, und kein Payload ist nötig. Eine einzelne HTML-Datei auf beliebiger Domain ist der gesamte Angriff.

Entscheidend ist der Unterschied zwischen Wildcard und reflektiertem Origin. Eine Wildcard auf einem öffentlichen, unauthentifizierten Lese-Endpunkt ist oft eine bewusste Bequemlichkeit: die Daten sind öffentlich, also könne jeder sie abrufen. Die Überlegung stimmt für die Daten und irrt sich beim Browser.

Darum kann ein einziger Befund eine lange Liste von Versionshinweisen übertreffen. Er entscheidet für jede künftige Injektion auf jeder Seite, ob der Angreifer die Antwort liest, ohne Fuss zu fassen.

TL;DR: Eine kommunale Content-Plattform auf modernem Stack ergab einen einzigen Befund höchster Schwere. Er brauchte keine Versionsprüfung und keinen Exploit; der Browser hat die Arbeit erledigt.

Befunde

SchweregradBefundNachweis
KRITISCHWildcard-Cross-Origin-Policy auf der öffentlichen DatenschnittstelleJede Seite auf jeder Domain kann die Antwort lesen
MITTELWildcard-Policy auch auf schreibfähige Pfade angewandtPermissiver Header nicht auf den Lese-Endpunkt begrenzt
MITTELWildcard kombiniert mit Credentials auf einer RouteAntwort mit angehängten Cookies lesbar
NIEDRIGKein Vary-Header auf dem reflektierten OriginCache-Poisoning-Fläche
NIEDRIGPreflight ohne Routen-Allowlist beantwortetMethoden- und Header-Aushandlung nicht begrenzt
INFOPlattform vollständig gepatchtKeine ausnutzbare Schwachstelle in der Anwendung selbst

Der Angriffspfad

Attacker page on any domainCross-origin fetch with credentialsTarget data interfaceWildcard policy returnedBrowser permits reading the responseData readable without any foothold

Reproduktion im Labor

Jeder Befehl unten zielt auf einen Lab-Container, den ich selbst kontrolliere. Nichts davon ist auf ein laufendes System gerichtet.

Die eigene Schnittstelle von einem fremden Origin aus prüfen

~/code/bash bash
# Ziel: Ihre eigene Labor-Schnittstelle
# 1. was sagt der Server für einen fremden Origin
$ curl -s -o /dev/null -D - -H 'Origin: https://evil.example' \
  'https://example.org/api/public/items' | grep -i 'access-control'

# 2. reflektiert er jeden Origin, auch eine nicht existierende Subdomain
$ curl -s -o /dev/null -D - -H 'Origin: https://angreifer-kontrolliert.example.org' \
  'https://example.org/api/public/items' | grep -i 'access-control'

# 3. und lassen sich Credentials anhängen
$ curl -s -o /dev/null -D - -H 'Origin: https://evil.example' \
  --cookie 'session=beliebig' 'https://example.org/api/private/items' | grep -i 'access-control'

Erkennung in eigener Infrastruktur

Die eigene Landschaft auf reflektierte Origin-Header durchsuchen

~/code/bash bash
$ for p in /api/public/items /api/private/items /graphql /api/me; do
    printf '%s -> %s\n' "$p" \
      "$(curl -s -o /dev/null -D - -H 'Origin: https://probe.example' "https://example.org$p" \
         | grep -i 'access-control-allow-origin' | tr -d '\r')"
done

# und auf die Kombination, die wirklich zählt
$ curl -s -o /dev/null -D - -X OPTIONS -H 'Origin: https://probe.example' \
  -H 'Access-Control-Request-Method: DELETE' -H 'Access-Control-Request-Headers: authorization' \
  'https://example.org/api/public/items' | grep -i 'access-control'

Behebung

Die Policy auf eine Allowlist begrenzen und niemals blind reflektieren

~/code/nginx nginx
# eine explizite Allowlist, serverseitig ausgewertet
map $http_origin $cors_origin {
    default                       "";
    "https://example.org"        $http_origin;
    "https://www.example.org"    $http_origin;
}

add_header Access-Control-Allow-Origin $cors_origin always;
add_header Vary Origin always;

# Credentials nur dort, wo der Endpunkt wirklich authentifiziert ist und die
# Origin-Liste bereits auf eigene Hosts begrenzt ist
# add_header Access-Control-Allow-Credentials "true" always;

Die korrekte Ablehnung statt eines permissiven Headers zurückgeben

~/code/ts ts
// eine Allowlist schlägt eine Wildcard, immer
const ALLOWED = new Set(['https://example.org', 'https://www.example.org']);

export function cors(req, res, next) {
  const origin = req.headers.origin;
  if (origin && ALLOWED.has(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
    res.setHeader('Vary', 'Origin');
  }
  // kein Origin-Header, wenn die Anfrage nicht von einem erlaubten Origin kommt
  next();
}

Sobald ein Angreifer Zugriff hat, kann er folgende Aktionen durchführen:

Wildcard-Cross-Origin-Policy auf der öffentlichen Datenschnittstelle
Wildcard-Policy auch auf schreibfähige Pfade angewandt
Wildcard kombiniert mit Credentials auf einer Route

Fazit — was ich daraus mitnehme

  • Eine zu permissive Cross-Origin-Policy ist ein Multiplikator für jeden anderen Bug.
  • Öffentliche Daten bedeuten nicht, von jeder Seite lesbar zu sein.
  • Nie einen Origin ohne Allowlist und ohne Vary reflektieren.
  • Ein Befund, der die Schwere aller anderen verändert, gehört an die Spitze des Berichts.

Nur zu Bildungszwecken. Alle reproduzierbaren Befehle zielen auf meine eigene Lab-Umgebung (localhost), nie auf ein laufendes System. © 2026