Europe/Berlin
--:--:--

FastAPI CORS: 5 Fehler und sichere Konfiguration

April 9, 2026 Als Backend-Entwickler mit einem Auge für IT-Security sehe ich immer wieder, wie die Cross-Origin Resource Sharing (CORS)-Konfiguration in FastAPI-Anwendungen entweder unterschätzt oder schlichtweg falsch implementiert wird. Auf meinem Weg zum Pentester habe ich gelernt, dass eine scheinbar harmlose CORS-Regel schnell zu einer kritischen Schwachstelle werden kann. Es geht nicht nur darum, dass Ihre Frontend-Anwendung funktioniert, sondern darum, Ihr Backend vor Client-seitigen Angriffen zu schützen. FastAPI selbst nutzt Starlettes CORSMiddleware, um diese essentiellen Sicherheitsmechanismen zu steuern. CORS ist ein Browser-Sicherheitsmechanismus, der im Kern dazu dient, den Zugriff auf Ressourcen auf einem Server von einer anderen Domain aus zu regulieren. Ohne CORS würde die Same-Origin Policy (SOP) des Browsers jede anfragende Domain blockieren, die nicht exakt der Domain des Servers entspricht. Das wäre aus Nutzersicht fatal, da moderne Webanwendungen oft über mehrere Subdomains verfügen oder Ressourcen von Drittanbietern einbinden. Die korrekte CORS-Konfiguration ist daher nicht nur eine Frage der Funktionalität, sondern essenziell, um Client-seitige Angriffe wie Cross-Site Request Forgery (CSRF) oder unbeabsichtigte Datenlecks zu verhindern. Jedes Mal, wenn ein Browser eine cross-origin Anfrage stellt, die potenziell Seiteneffekte hat oder nicht-standardmäßige Header verwendet (z.B. POST, PUT, DELETE, oder mit Authorization-Header), sendet er eine sogenannte "Preflight-Anfrage" mit der HTTP-Methode OPTIONS. Der Server muss auf diese Preflight-Anfrage mit den korrekten CORS-Headern antworten, bevor die eigentliche Anfrage gesendet wird. Wenn die Middleware hier keine korrekte Antwort liefert, scheitert der CORS-Handshake und die eigentliche Anfrage wird vom Browser gar nicht erst abgeschickt. Ich habe in verschiedenen Projekten immer wieder ähnliche Muster bei der CORS-Konfiguration beobachtet, die unnötige Risiken schaffen. Hier sind die gängigsten: Dieser Fehler ist der absolute Klassiker und das erste, wonach ich bei einem schnellen Security-Check suche. allow_origins=['*'] erlaubt jeder beliebigen Domain, Anfragen an Ihr Backend zu senden. Viele Entwickler nutzen dies während der Entwicklung, vergessen es aber, für die Produktion anzupassen. Die Begründung ist oft: "Mein Frontend läuft auf https://example.com und mein Backend auf https://api.example.com, da ist das doch kein Problem." Doch diese Annahme ist trügerisch. Wenn Ihre API keine authentifizierten oder sensiblen Endpunkte bietet und wirklich für jeden zugänglich sein soll, mag ein Wildcard akzeptabel sein. Sobald jedoch Authentifizierung oder personenbezogene Daten ins Spiel kommen, öffnen Sie Tür und Tor für Angriffe. Stellen Sie sich vor, ein Angreifer hostet eine bösartige Seite. Wenn Ihre API allow_origins=['*'] hat, kann der Angreifer von seiner Seite aus Anfragen an Ihr Backend senden. Wenn diese Anfragen Authentifizierungs-Cookies mitliefern (was sie tun, wenn allow_credentials=True ist), kann der Angreifer Aktionen im Namen des Nutzers ausführen, ohne dessen Wissen. Das ist klassisches CSRF, nur noch einfacher, weil keine weiteren Exploits nötig sind. Dies ist die gefährlichste Konfiguration, die mir in Projekten begegnet ist. Wenn Sie allow_origins=['*'] und allow_credentials=True setzen, ermöglichen Sie es jeder externen Domain, Anfragen mit Cookies, HTTP-Authentifizierungs-Headern oder TLS-Client-Zertifikaten an Ihr Backend zu stellen. Ein Angreifer könnte eine Seite erstellen, die im Hintergrund Anfragen an Ihre API sendet und dabei die Session-Cookies Ihrer Nutzer (die der Browser automatisch mitschickt) nutzt. Der Angreifer kann dann im Namen des Nutzers sensible Aktionen ausführen oder sogar Daten auslesen, die normalerweise nur der Nutzer selbst sehen dürfte. Diese Konfiguration habe ich einmal in einem internen Testsystem entdeckt, das später produktiv gehen sollte. Die Entwickler hatten es für Testzwecke so eingerichtet und schlicht vergessen, es zu korrigieren. Eine kleine Änderung hätte katastrophale Folgen haben können. Ähnlich wie bei allow_origins, sollten auch allow_methods und allow_headers auf das absolut Notwendige beschränkt werden. allow_methods=['*'] oder allow_headers=['*'] ist zwar weniger kritisch als eine Wildcard bei den Origins, erhöht aber die Angriffsfläche. Jede erlaubte Methode oder jeder Header, der nicht benötigt wird, ist eine potenzielle Quelle für Missverständnisse oder zukünftige Schwachstellen. Besonders bei nicht-standardmäßigen Headern, wie beispielsweise einem X-API-Key oder X-Requested-With, ist es wichtig, diese explizit in allow_headers aufzuführen. Andernfalls blockiert die CORS-Regel diese Header, und Ihr Frontend kann die API nicht korrekt nutzen. Ich habe oft gesehen, dass APIs benutzerdefinierte Header erwarten, aber diese nicht in der CORS-Konfiguration aufgeführt wurden, was zu frustrierenden "Blocked by CORS policy"-Fehlern führte. Der max_age-Parameter in der CORSMiddleware steuert, wie lange Browser die Antworten auf Preflight-Anfragen (OPTIONS) cachen dürfen. Ein zu hoher Wert (z.B. mehrere Stunden oder Tage) kann dazu führen, dass Konfigurationsänderungen an Ihren CORS-Regeln länger brauchen, um auf Client-Seite wirksam zu werden. Wenn Sie eine Sicherheitslücke in Ihrer CORS-Konfiguration beheben, kann es also sein, dass Browser bestehende Caches nutzen und die alte, unsichere Konfiguration weiterhin anwenden. Ein zu kurzer Wert (z.B. nur wenige Sekunden oder gar nicht gesetzt) hingegen führt dazu, dass Browser für fast jede cross-origin Anfrage eine Preflight-Anfrage senden müssen. Das erhöht die Netzwerklast und verlangsamt die Performance Ihrer Anwendung unnötig. Hier ist ein ausgewogener Wert (z.B. 600 bis 3600 Sekunden / 10 Minuten bis 1 Stunde) oft der beste Kompromiss. Wie bereits erwähnt, sind Preflight-Anfragen mit der HTTP-Methode OPTIONS ein fundamentaler Bestandteil des CORS-Handshakes. Die CORSMiddleware in FastAPI ist dafür konzipiert, diese Anfragen automatisch zu verarbeiten und die notwendigen CORS-Header bereitzustellen. Wenn Sie jedoch eigene Routen für OPTIONS-Methoden definieren oder die Middleware falsch konfigurieren, kann es passieren, dass diese Preflight-Anfragen nicht korrekt beantwortet werden. Das führt dazu, dass der Browser die eigentliche Anfrage blockiert, noch bevor sie Ihr Anwendungs-Logik erreicht. Um die genannten Fallen zu vermeiden und eine robuste, sichere CORS-Konfiguration zu etablieren, empfehle ich folgende Vorgehensweisen, die ich selbst in Projekten umsetze: Definieren Sie immer explizit jede einzelne Domain, die auf Ihre API zugreifen darf. Für lokale Entwicklungsumgebungen ist http://localhost:3000 oder ähnliches natürlich in Ordnung, aber trennen Sie strikt zwischen Entwicklung und Produktion.
Python
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
import os

app = FastAPI(title="Sichere API")

# Sichere und spezifische CORS-Konfiguration für Produktion
# Hier könnten Hostnames aus Umgebungsvariablen kommen
# Dies ermöglicht eine einfache Anpassung in verschiedenen Umgebungen (Test, Staging, Prod)
env_allowed_hosts_str = os.environ.get("CORS_ALLOWED_HOSTS", "")
env_allowed_hosts = [f"https://{h.strip()}" for h in env_allowed_hosts_str.split(',') if h.strip()]

# Liste der explizit erlaubten Origins
origins = [
    "https://meine-frontend-domain.com",
    "https://admin.meine-frontend-domain.com",
    "http://localhost:3000" # Nur für lokale Entwicklung und sollte in Produktion entfernt/geändert werden
]

# Füge dynamische Origins hinzu und entferne Duplikate
origins.extend(env_allowed_hosts)
origins = list(set(origins)) # Entfernt Duplikate

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True, # WICHTIG: Nur mit spezifischen Origins verwenden!
    allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], # Nur benötigte Methoden
    allow_headers=["X-Requested-With", "Content-Type", "Accept", "Authorization", "X-API-Key"], # Nur benötigte Header
    max_age=600 # Cache Preflight-Anfragen für 10 Minuten
)

@app.get("/items")
async def read_items():
    return {"message": "Sichere Daten vom Backend"}

@app.post("/items")
async def create_item(item: dict):
    return {"message": f"Artikel erstellt: {item['name']}"}

# DIES IST EINE FALSCHE UND GEFÄHRLICHE KONFIGURATION! NIEMALS IN PRODUKTION VERWENDEN!
# app.add_middleware(
#     CORSMiddleware,
#     allow_origins=["*"],
#     allow_credentials=True, # GROSSES SICHERHEITSRISIKO! Ermöglicht CSRF und Datenlecks von jeder Domain.
#     allow_methods=["*"],
#     allow_headers=["*"],
#     max_age=0 # Auch wenn max_age 0 wäre, bleibt die Grundkonfiguration extrem unsicher.
# )
In größeren Projekten, besonders in Cloud-Umgebungen mit mehreren Staging- oder Feature-Branches, kann es mühsam sein, jede URL manuell in der origins-Liste zu pflegen. Hier bietet es sich an, die Liste dynamisch zu generieren, beispielsweise über Umgebungsvariablen wie im obigen Beispiel gezeigt, oder mit intelligenten Regex-Mustern. Wichtig ist dabei, dass die Logik nur explizit vertrauenswürdige Hosts erlaubt und keine unsicheren Wildcards in der Regex selbst verwendet werden. Ein Ansatz könnte sein, eine CORS_ALLOWED_HOSTS Umgebungsvariable zu definieren, die eine komma-separierte Liste von Hostnames enthält. Ihr FastAPI-Dienst liest diese Variable dann aus und konstruiert die origins-Liste zur Laufzeit. So bleibt die Konfiguration flexibel, aber sicher. Um zu überprüfen, ob Ihre CORS-Konfiguration korrekt ist, können Sie curl verwenden, um Preflight- und reguläre Anfragen von einer simulierten Drittanbieter-Origin zu senden. Achten Sie auf die Access-Control-*-Header in der Antwort.
Bash
# Starten Sie Ihre FastAPI-Anwendung (z.B. mit: uvicorn main:app --reload)
# Stellen Sie sicher, dass Ihre Anwendung auf http://localhost:8000 läuft

# Test einer Preflight-Anfrage (OPTIONS) von einer erlaubten Origin
curl -v -X OPTIONS http://localhost:8000/items \
-H "Origin: https://meine-frontend-domain.com" \
-H "Access-Control-Request-Method: GET" \
-H "Access-Control-Request-Headers: Authorization,Content-Type"

# Erwartete Antwort-Header (Auszug):
# < Access-Control-Allow-Origin: https://meine-frontend-domain.com
# < Access-Control-Allow-Credentials: true
# < Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
# < Access-Control-Allow-Headers: X-Requested-With, Content-Type, Accept, Authorization, X-API-Key
# < Access-Control-Max-Age: 600

# Test einer GET-Anfrage von einer erlaubten Origin
curl -v http://localhost:8000/items \
-H "Origin: https://meine-frontend-domain.com"

# Erwartete Antwort-Header (Auszug):
# < Access-Control-Allow-Origin: https://meine-frontend-domain.com
# < Access-Control-Allow-Credentials: true

# Test einer GET-Anfrage von einer NICHT-erlaubten Origin (dies sollte vom Browser blockiert werden)
# In der Konsole des Browsers sehen Sie einen CORS-Fehler.
# curl -v http://localhost:8000/items \ 
# -H