~/blog/openapi-docs-public-endpoint-mapPOSTED
von Shady Nathan Tawfik · 22. August 2026 · 4 min

Die API-Dokumentation war öffentlich, und sie war die beste Karte, die man sich wünschen kann

Eine mit einem automatisierten Framework gebaute Anwendung lieferte interaktive Dokumentation und ein maschinenlesbares Schema an einem bekannten Pfad. Beides war standardmäßig aktiviert und keines hinter einem Login.

Interaktiv[blog-2.0]

TL;DR: Die API-Dokumentation war öffentlich, und sie war die beste Karte, die man sich wünschen kann: jeder Pfad, Feldname und Validierungsregel, auch die administrativen Routen, auf die keine Oberfläche verweist. Ich zeige, wie man sie außerhalb der Entwicklung abschaltet.

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.

Ein Softwareprodukt auf Basis eines automatisierten Anwendungsframeworks, mit separatem Client-Frontend, Drittanbieter-Diensten für Zahlungen, KI und E-Mail sowie Object Storage für Medien. Geprüft wurden öffentliche Schema- und Dokumentationsendpunkte, Header-Policy, Skript-Integrität, E-Mail-Authentifizierung und die Vertrauenskette.

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:

Frameworks, die einen Client aus einer Typdefinition generieren, erzeugen gerne Dokumentation aus derselben Definition. Das ist ein Feature: Die Dokumentation kann nicht von der Implementierung abweichen. Die Folge ist, dass das Veröffentlichen der Dokumentation eine einzelne Umgebungsentscheidung ist, und der Standard ist in mehreren verbreiteten Frameworks, sie in jeder Umgebung einschliesslich Produktion zu veröffentlichen.

Eine interaktive Dokumentationsseite ist mehr als eine Bequemlichkeit. Sie rendert das echte Schema mit Feldnamen, Typen, Pflichtfeldern und Validierungsregeln, also genau die Information, die ein Angreifer braucht, um korrekte Anfragen zu bauen.

Das tiefere Muster sind Standardkonfigurationen. Jede Kontrolle in diesem Mandat war ein Framework-Standard statt einer Entscheidung: Dokumentation an, Inline-Skripte in der Policy erlaubt, Analytics von einer selbst gehosteten Instanz auf einem separaten Server, Medien von einem Storage-Endpunkt mit Wildcard-Origin-Policy.

TL;DR: Eine mit einem automatisierten Framework gebaute Anwendung lieferte interaktive Dokumentation und ein maschinenlesbares Schema an einem bekannten Pfad. Beides war standardmäßig aktiviert und keines hinter einem Login.

Befunde

SchweregradBefundNachweis
MITTELInteraktive Dokumentation öffentlich ausgeliefertVolles Schema mit Feldnamen und Validierungsregeln
MITTELMaschinenlesbares Schema listet interne und administrative PfadeVollständiges Routen-Inventar inklusive Pfade ohne Oberflächenbezug
MITTELInline-Skripte in der Content Security Policy erlaubtKeine Einschränkung der Inline-Ausführung
MITTELDrittanbieter-Skript ohne Integrität und von einem separaten HostKompromittierung dieses Hosts bedeutet volle Kontrolle der Seite
NIEDRIGMedien-Endpunkt mit Wildcard-Origin-Policy ausgeliefertJeder Origin darf gespeicherte Dateien lesen
NIEDRIGTransport-Policy ohne Preload und Subdomain-ScopeHardening vorhanden, aber unvollständig

Der Angriffspfad

Application type definitionsGenerated clientGenerated documentationPublished by default in productionFull route inventoryCorrect requests built without guessing

Reproduktion im Labor

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

Die bekannten Dokumentationspfade des eigenen Dienstes prüfen

~/code/bash bash
# Ziel: Ihr eigener Labordienst auf localhost
$ for p in /docs /redoc /openapi.json /api-docs /swagger /swagger.json; do
    printf '%-16s %s\n' "$p" "$(curl -so /dev/null -w '%{http_code}' "http://localhost:8000$p")"
done

# wenn das Schema öffentlich ist, das Routen-Inventar daraus lesen
$ curl -s http://localhost:8000/openapi.json | python3 -c '
import json,sys
spec = json.load(sys.stdin)
for path, ops in spec.get("paths", {}).items():
    print(path, ",".join(k.upper() for k in ops))' | head -40

Erkennung in eigener Infrastruktur

Inline-Ausführungserlaubnisse in der eigenen Policy suchen

~/code/bash bash
$ curl -sI https://example.org/ | grep -i 'content-security-policy' | tr ';' '\n' \
  | grep -E 'script-src|unsafe-inline|unsafe-eval|\*'

Jeden Drittanbieter-Skript-Origin auflisten, den Ihre Seite lädt

~/code/bash bash
$ curl -s https://example.org/ | grep -oE '<script[^>]*src="https?://[^"]*"[^>]*>' \
  | grep -oE 'src="https?://[^/]+' | sort -u

# und wie viele davon Integrität tragen
$ curl -s https://example.org/ | grep -oE '<script[^>]*src="https?://[^"]*"[^>]*>' \
  | grep -c integrity

Behebung

Dokumentation außerhalb der Entwicklung abschalten

~/code/python python
# FastAPI: die Dokumentation ist opt-in, also Produktion opt-out machen
import os

ENV = os.environ.get("APP_ENV", "production")

app = FastAPI(
    title="Internal service",
    # beide stehen standardmäßig auf True; sie nicht zu setzen veröffentlicht das Schema
    docs=None if ENV == "production" else "/docs",
    redoc=None if ENV == "production" else "/redoc",
    openapi_url=None if ENV == "production" else "/openapi.json",
)

# und in einem Test absichern, damit eine spätere Default-Änderung es nicht zurückbringt
def test_no_schema_in_production(client):
    app.openapi_url = None
    assert client.get("/openapi.json").status_code == 404
    assert client.get("/docs").status_code == 404

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

Interaktive Dokumentation öffentlich ausgeliefert
Maschinenlesbares Schema listet interne und administrative Pfade
Inline-Skripte in der Content Security Policy erlaubt
Drittanbieter-Skript ohne Integrität und von einem separaten Host

Fazit — was ich daraus mitnehme

  • Ein generiertes Schema ist eine Angriffsflächenkarte für einen Request.
  • Dokumentation ist im Framework opt-in und in Ihrer Umgebung opt-out. Setzen Sie es explizit.
  • Sichern Sie die Abwesenheit im Test ab; eine Default-Änderung kündigt sich nicht an.
  • Auditieren Sie die Drittanbieter-Skript-Origins. Ein eigener Server ist keine Kontrolle.

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