Wie diese Prüfung funktioniert
Scanaris fordert deine Startseite erneut an, mit einem Origin-Header einer erfundenen Domain, so wie es der Browser täte, wenn eine andere Website dich aufrufen wollte, und schaut, was dein Server antwortet. Liefert Access-Control-Allow-Origin genau diesen Origin zurück und steht zusätzlich Access-Control-Allow-Credentials auf true, öffnet es einen Fund mit hohem Schweregrad: Dein Server spiegelt jeden Origin mit Credentials. Antwortet er mit dem Platzhalter * zusammen mit Credentials, öffnet es einen mit niedrigem Schweregrad: Browser lehnen diese Kombination ab, aber sie verrät eine blind gemachte Konfiguration. Getestet wird nur die gescannte URL; lebt deine API auf einem anderen Pfad oder einer anderen Domain, teste sie mit dem Befehl unten.
Was ein Angreifer damit tut
Er baut auf irgendeiner Domain eine Seite mit einem fetch an deine API und credentials: 'include'. Besucht einer deiner Nutzer mit offener Sitzung diese Seite, hängt der Browser seine Cookies an, dein Server antwortet mit den Daten des Nutzers, und weil die CORS-Richtlinie es erlaubt, liest die Seite des Angreifers sie aus. Profil, Bestellungen, Nachrichten, was die API eben liefert. Der Nutzer merkt nichts: Es passiert im Hintergrund.
Warum es passiert
CORS existiert, damit der Browser domainübergreifende Lesezugriffe standardmäßig blockiert. Scheitert ein Aufruf in der Entwicklung (App auf einem Port, API auf einem anderen), lautet die schnelle Lösung aus Tutorials und KI-Tools „alle Origins erlauben", und damit Cookies funktionieren, kommt „mit Credentials" dazu. In Produktion heißt das: Der Schutz wurde abgeschaltet. Mehr dazu in falsch konfiguriertes CORS: der stille Fehler, der deine Nutzer offenlegt.
Wie du es behebst
- Explizite Liste von Origins. Nur die Domains, die deine API wirklich aus dem Browser aufrufen. In Express:
cors({ origin: ['https://app.deinedomain.com'], credentials: true }). In Next.js vergleichst du im Route Handler denOriginmit deiner Liste, bevor du ihn zurückgibst. In Nuxt mitrouteRulesund der Optioncorsoder einer Server-Middleware. - Spiegle nie den Origin. Den Wert von
Originin die Antwort zu kopieren, „weil dann alles läuft", ist genau der Fehler, den die Prüfung misst. - Brauchst du keine Cookies über Domains hinweg, entferne
Access-Control-Allow-Credentials. Ohne Credentials ist ein*auf einer öffentlichen Nur-Lese-API akzeptabel. - Füge Vary: Origin hinzu, wenn die Antwort vom Origin abhängt, damit kein Cache einer Domain die Antwort einer anderen liefert.
- Cookies mit SameSite=Lax oder Strict als zweite Barriere: Selbst wenn CORS versagt, würde der Browser sie von einer anderen Website nicht mitschicken.
Prüf, ob es behoben ist
curl -sI -H "Origin: https://evil.example" https://deinedomain.com/api/me | grep -i "access-control"
Richtig ist, dass Access-Control-Allow-Origin für diesen Origin gar nicht erscheint oder mit deiner Domain statt mit evil.example. Teste auch mit deiner eigenen Domain im Origin, um zu bestätigen, dass deine App weiter funktioniert.
Häufige Fragen
Meine API ist öffentlich und ohne Login. Ist das ein Problem?
Nein, solange es keine Cookies und keine Authorization-Header gibt. Access-Control-Allow-Origin: * ohne Credentials ist die richtige Konfiguration für eine offene API.
Ich habe mehrere Subdomains, die die API aufrufen.
Liste sie alle auf oder prüf den Origin gegen ein strenges Muster deiner Root-Domain. Prüf nie mit „enthält deinedomain.com": deinedomain.com.evil.example besteht diesen Test.
Und localhost für die Entwicklung?
Nimm es nur in der Entwicklungsumgebung auf, über eine Umgebungsvariable. Ein http://localhost:3000 in der Produktionsliste ist ein Origin, den jedes Programm auf dem Rechner des Nutzers verwenden kann.