Wie diese Prüfung funktioniert
Scanaris schaut sich die Hauptantwort deiner Seite an und prüft zwei Bedingungen zugleich. Erstens: Die Antwort setzt mindestens ein Cookie (Set-Cookie), dessen Name mit sess, sid, auth, token, jwt, connect.sid, phpsessid oder asp.net beginnt, also ein Cookie, das mit hoher Wahrscheinlichkeit eine Sitzung identifiziert. Zweitens: Dieselbe Antwort trägt Cache-Control mit der Direktive public. Nur wenn beides zutrifft, öffnet sie einen Befund mit mittlerem Schweregrad, der den Cookie-Namen und den Header-Wert nennt. Das ist absichtlich streng: Ein Einstellungs-Cookie mit öffentlichem Cache oder ein Sitzungs-Cookie ohne public werden nicht gemeldet.
Warum das wichtig ist
Cache-Control: public ist eine ausdrückliche Anweisung an jeden geteilten Cache (ein CDN, einen Reverse-Proxy wie Varnish oder nginx, den Proxy eines Unternehmens oder Netzbetreibers), dass diese Antwort gespeichert und an andere Nutzer ausgeliefert werden darf. Enthält dieselbe Antwort ein Sitzungs-Set-Cookie, kann der Cache das Cookie mit dem Body speichern und dem nächsten Besucher geben. Dieser Besucher kommt mit der Sitzung des vorherigen rein, ohne Passwort, und sieht dessen Daten. Es ist die Art Fehler, die man entdeckt, wenn ein Kunde schreibt, er sei „im Konto von jemand anderem gelandet". Es ist Banken und großen Händlern passiert, immer durch dieselbe Kombination: personalisierter Inhalt, der als cachebar markiert ist.
Warum das passiert
Meist durch eine zu breite Cache-Regel: ein add_header Cache-Control "public, max-age=3600" in nginx, das auf alle Routen wirkt, auch auf die, die Sitzungen anlegen; eine „Cache Everything"-Regel in Cloudflare; oder ein routeRules-Eintrag mit Cache-Headern für '/**' in einer App, die auf der Startseite Cookies setzt. Die meisten CDNs weigern sich standardmäßig, Antworten mit Set-Cookie zu speichern, und nginx ebenso, solange du ihm nicht sagst, diesen Header zu ignorieren; aber ein explizites public bist du, der ihnen sagt, es zu tun.
So behebst du es
- Antworten, die eine Sitzung anlegen oder erneuern, tragen
Cache-Control: private, no-store. Das machenexpress-session, Laravel, Django und Rails standardmäßig; siehst dupublic, hat es eine spätere Schicht hinzugefügt. - Trenne Cache-Regeln nach Routentyp.
publicmit langemmax-agefür statische Dateien mit Hash im Namen (/_nuxt/,/_next/static/,/assets/);privateoderno-storefür HTML, das vom Nutzer abhängt. In nginx nutzt du getrenntelocation-Blöcke; in Nuxt und Next.js Regeln pro Route statt einer globalen. - Setz das Sitzungs-Cookie nicht auf öffentlichen, cachebaren Seiten. Braucht die Startseite keine Sitzung, soll sie keine anlegen: Erzeuge das Cookie beim Login oder beim Betreten des Dashboards.
- Nutzt du ein CDN mit „Cache Everything" oder Ähnlichem, füge eine Regel hinzu, Antworten mit
Set-Cookieund authentifizierte Routen nicht zu cachen. Und ergänzeVary: Cookieals zusätzliches Netz, auch wenn es das Vorherige nicht ersetzt.
Prüf, ob es behoben ist
curl -sI https://deinedomain.de | grep -iE "^(set-cookie|cache-control|vary):"
curl -sI https://deinedomain.de/dashboard | grep -iE "^(set-cookie|cache-control):"
Gibt es ein Set-Cookie mit Sitzungsnamen, muss das Cache-Control dieser Antwort private, no-store oder no-cache sein, nie public. Führ die zweite Anfrage mit und ohne dein Sitzungs-Cookie aus: Die authentifizierte Antwort darf ohne Cookie nie erscheinen.
Häufige Fragen
Mein CDN sagt, es cacht keine Antworten mit Cookies. Also ein Fehlalarm?
Das Leck passiert heute vermutlich nicht, aber du hängst von einem Standardverhalten ab, das eine neue Regel oder ein Anbieterwechsel abschalten kann. Der Header sagt, es sei öffentlich; korrigiere den Header und hör auf, dich auf die Ausnahme zu verlassen.
Und Analyse- oder Einwilligungs-Cookies?
Die werden nicht gemeldet: Nur Namen zählen, die auf eine Sitzung hindeuten. Ein Einwilligungs-Cookie auf einer öffentlichen, gecachten Seite ist normal. Wie Sitzungs-Cookies konfiguriert sein sollten (Secure, HttpOnly, SameSite), prüft unsichere Cookies.
Meine Sitzung ist ein Token im localStorage, kein Cookie.
Dann gilt diese Prüfung nicht, aber Token im Speicher schon: Ein aus JavaScript erreichbarer Token hat andere Probleme.