Layer 8Ehrlicher Tech-Support

Anmeldung im eingebetteten Fenster scheitert: Cookie kommt nicht mit

Login oder Formular bricht ab, sobald es eingebettet läuft oder von außen verlinkt ist. Der Browser schickt das Cookie im Fremdkontext nicht mit.

Aufwand:
ca. 40 Min.
Voraussetzung:
Ein Browser mit Entwicklerwerkzeugen, Zugriff auf die Serverkonfiguration oder den Anwendungscode, Terminal mit curl
Gilt für:
Alle aktuellen Browser, RFC 6265 und der Nachfolgeentwurf draft-ietf-httpbis-rfc6265bis

Das Symptom

Etwas, das jahrelang lief, läuft nicht mehr — und zwar nur in bestimmten Situationen. Das Bezahlfenster im Rahmen der Bestellseite meldet, die Sitzung sei abgelaufen. Der Kalender, der auf der Intranetseite eingebettet ist, zeigt eine Anmeldemaske, obwohl man angemeldet ist. Ein Link aus der Rundmail landet auf der Startseite statt im Kundenkonto. Öffnet man dieselbe Adresse direkt in einem eigenen Tab, funktioniert alles einwandfrei. Im Privatmodus ist es reproduzierbar kaputt.

Die „Layer 8“-Wahrheit

Ein Cookie ist nichts weiter als ein Zettel, den der Server dem Browser mitgibt und den der Browser bei künftigen Anfragen wieder vorlegt. Das ist der gesamte Mechanismus, mit dem das Web sich merkt, wer du bist — HTTP selbst hat kein Gedächtnis. Und genau dieser Zettel wird seit einigen Jahren deutlich zurückhaltender vorgelegt.

Der Grund dafür steht schon in der ursprünglichen Cookie-Spezifikation von 2011, in einem Abschnitt mit der Überschrift Third-Party Cookies: Wenn eine Seite Inhalte von fremden Servern nachlädt, können diese fremden Server Cookies setzen und den Besucher damit über Seitengrenzen hinweg verfolgen — ohne dass er diese Server je bewusst besucht hätte. Dagegen wurde das Attribut SameSite geschaffen. Es begrenzt, wie der Nachfolgeentwurf es formuliert, den Geltungsbereich eines Cookies auf Anfragen, die zur selben Site gehören.

Damit sind drei Zustände möglich, und alle drei erzeugen ein anderes Symptom. Strict heißt: nur bei Anfragen innerhalb derselben Site. Wer von außen hereinklickt, kommt abgemeldet an — der Entwurf beschreibt diesen Fall selbst als Quelle erheblicher Verwirrung. Lax heißt: innerhalb derselben Site, und zusätzlich bei Navigationen auf oberster Ebene. Das ist der Grund, warum der Link aus der E-Mail funktioniert, das eingebettete Fenster aber nicht. None heißt: immer — und nur dieser Wert trägt eingebettete Anmeldungen, Bezahlfenster und Widgets.

Hier liegt die Falle, in die fast jeder tappt: SameSite=None ohne das Attribut Secure ist kein abgeschwächtes Cookie, sondern gar keines. Der Browser wirft es komplett weg. Wer die Umstellung also macht und nur die halbe Zeile setzt, verschlimmert die Lage, statt sie zu verbessern — und sieht das oft erst in der Produktion, weil lokal über http://localhost entwickelt wird.

Und dann bleibt eine unbequeme Wahrheit: SameSite ist eine Bitte des Servers, keine Garantie. Schon RFC 6265 stellt es Browsern ausdrücklich frei, Cookies im Fremdkontext überhaupt nicht zu senden. Was heute mit None; Secure noch läuft, kann morgen an einer Browsereinstellung scheitern. Deshalb ist die beste Antwort auf dieses Problem selten ein besseres Cookie-Attribut, sondern ein Ablauf, der ohne Fremdkontext auskommt: umleiten statt einbetten. Dann ist alles dieselbe Site, und es gibt nichts mehr zu blockieren.

Die echte, verifizierte Lösung

1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:

  1. 1.

    Zuerst feststellen, ob das Cookie überhaupt gesetzt wird

    Zwei völlig verschiedene Fehler sehen gleich aus: Das Cookie wird nie gesetzt, oder es wird gesetzt und später nicht mitgeschickt. Die Antwort des Servers zeigt den Unterschied. Der Befehl gibt alle Kopfzeilen der Anmeldeantwort aus und filtert die Cookie-Zeilen heraus — sie enthalten auch die gesetzten Attribute.

    curl -sS -D - -o /dev/null https://example.com/login | grep -i "^set-cookie:"
    
  2. 2.

    In den Entwicklerwerkzeugen nachsehen, was der Browser davon behalten hat

    Mit F12 die Entwicklerwerkzeuge öffnen, im Reiter Netzwerk die betroffene Anfrage anklicken und die Cookies ansehen. Blockierte oder verworfene Cookies werden dort gesondert gekennzeichnet, meist mit einem Warnsymbol und einer Begründung. Das ist die verlässlichste Stelle: Sie zeigt nicht, was der Server wollte, sondern was der Browser tatsächlich getan hat.

  3. 3.

    Den Zusammenhang bestimmen, in dem die Anfrage läuft

    Entscheidend ist nicht die Domain des Cookies, sondern das Verhältnis zwischen der Domain in der Adresszeile und der Domain, die das Cookie braucht. Sind beide dieselbe Site, ist die Anfrage `same-site` und alle drei Werte funktionieren. Sind sie verschieden — eingebettetes Fenster, Anmeldeverfahren über einen fremden Anbieter, Bezahlseite im Rahmen —, ist sie `cross-site`, und nur `SameSite=None` wird noch mitgeschickt. Ein Aufruf über einen Link von außen ist ein Sonderfall: eine `cross-site`-Navigation auf oberster Ebene, bei der `Lax` mitgeht und `Strict` nicht.

  4. 4.

    Das Cookie korrekt für den Fremdkontext auszeichnen

    Braucht der Ablauf das Cookie tatsächlich im Fremdkontext, muss `SameSite=None` gesetzt sein — und zwingend zusammen mit `Secure`, sonst verwirft der Browser das Cookie vollständig. `HttpOnly` gehört bei Sitzungs-Cookies zusätzlich dazu, damit kein Skript sie auslesen kann. So sieht die vollständige Kopfzeile aus:

    Set-Cookie: sid=ABC123; Path=/; Secure; HttpOnly; SameSite=None
    
  5. 5.

    Prüfen, dass die Auslieferung wirklich über HTTPS läuft

    `Secure` bedeutet, dass der Browser das Cookie nur über eine verschlüsselte Verbindung annimmt und sendet. Läuft auch nur ein Schritt der Kette über `http://` — eine Weiterleitung, ein Zwischenserver, die lokale Entwicklungsumgebung —, fällt das Cookie lautlos weg. Der Befehl ruft die Seite bewusst unverschlüsselt auf: Es muss ein Statuscode 301 oder 308 mit einer `Location`-Zeile auf `https://` zurückkommen. Antwortet dort stattdessen die Anwendung selbst mit 200, wird ein Teil des Ablaufs unverschlüsselt bedient — und genau dort verschwindet das Cookie.

    curl -sSI http://example.com/login | head -5
    
  6. 6.

    Wo möglich den Fremdkontext ganz vermeiden

    Die haltbarste Lösung ist, das Problem nicht zu lösen, sondern loszuwerden. Statt eines eingebetteten Rahmens auf fremder Domain leitet man den Besucher auf die eigene Domain um und nach getaner Arbeit zurück — dann ist jede Anfrage `same-site` und keine Browsereinstellung kann dazwischenfunken. Für Anmeldungen über Dritte ist das ohnehin das übliche Verfahren.

  7. 7.

    Bei Links von außen das Zwei-Cookie-Modell erwägen

    Wenn Nutzer regelmäßig aus E-Mails oder von Partnerseiten kommen und dann ausgeloggt landen, steht das Sitzungs-Cookie auf `Strict`. Der Nachfolgeentwurf zu RFC 6265 empfiehlt für diesen Fall zwei getrennte Cookies: eines mit `Lax` für den lesenden Zugriff, damit der Besucher überhaupt ankommt, und eines mit `Strict` für alles, was Daten verändert. Fehlt das zweite, wird vor der Aktion erneut angemeldet.

Häufige Fragen (FAQ)

?Was ist ein „Drittanbieter-Cookie“ eigentlich genau?

Ein Cookie ist immer an eine Domain gebunden. Entscheidend ist nicht, welche Domain das Cookie gesetzt hat, sondern in welchem Zusammenhang es später mitgeschickt werden soll. Steht `shop.example` in der Adresszeile und lädt die Seite ein Bezahlfenster von `zahlung.example`, dann ist alles, was `zahlung.example` an Cookies braucht, aus Sicht des Browsers ein Cookie im Fremdkontext. RFC 6265 beschreibt genau dieses Muster und warum es Ärger macht: „Particularly worrisome are so-called 'third-party' cookies. In rendering an HTML document, a user agent often requests resources from other servers (such as advertising networks). These third-party servers can use cookies to track the user even if the user never visits the server directly.“ Weil dieselbe Technik Tracking und Bezahlvorgänge trägt, trifft die Einschränkung beide.

?Warum funktioniert die Anmeldung, wenn ich die Adresse direkt eintippe, aber nicht über einen Link aus einer E-Mail?

Weil dann eine Navigation von einer fremden Seite aus stattfindet und das Sitzungs-Cookie auf `SameSite=Strict` steht. Der Nachfolgeentwurf zu RFC 6265 beschreibt dieses Missverständnis als eigenes Kapitel: Ein Nutzer klickt in seinem Postfach auf einen Link zu `https://projects.example/secret/project` und erwartet den Inhalt, den er sehen darf — „but if https://projects.example has marked their session cookies as SameSite=Strict, then this cross-site navigation won't send them along with the request … and the user will be quite confused.“ Der empfohlene Ausweg im Entwurf ist ein Zwei-Cookie-Modell: ein Lese-Cookie mit `Lax`, ein Schreib-Cookie mit `Strict`, das vor jeder verändernden Aktion eine erneute Anmeldung erzwingt.

?Ich habe `SameSite=None` gesetzt und es geht trotzdem nicht. Was fehlt?

Mit hoher Wahrscheinlichkeit das Attribut `Secure`. Der Nachfolgeentwurf lässt daran keinen Interpretationsspielraum: „If the cookie's 'same-site-flag' is 'None', abort this algorithm and ignore the cookie entirely unless the cookie's secure-only-flag is true.“ Übersetzt: Ein Cookie mit `SameSite=None` ohne `Secure` wird nicht etwa eingeschränkt, sondern vollständig verworfen — es existiert für den Browser nie. Und `Secure` wiederum wirkt nur über HTTPS. Wer lokal über `http://localhost` entwickelt und in der Produktion über HTTPS ausliefert, sieht das Problem deshalb erst nach dem Deployment.

?Nützt `SameSite=None; Secure` denn immer?

Nein, und das ist der Punkt, an dem viele Anleitungen aufhören. `SameSite` ist eine Angabe, die der Server macht — der Nachfolgeentwurf betont ausdrücklich, dass dieser Schutz servergesteuert ist. Unabhängig davon entscheiden Browser eigenständig, ob sie Cookies im Fremdkontext überhaupt noch zulassen; schon RFC 6265 räumt ihnen das ein: „A user agent MAY omit the Cookie header in its entirety.“ Wo Browser oder ein Privatmodus Fremdkontext-Cookies grundsätzlich sperren, hilft kein Attribut. Dann bleibt nur, den Ablauf so umzubauen, dass er ohne Fremdkontext auskommt — etwa über eine Weiterleitung auf die eigene Domain statt eines eingebetteten Rahmens.

Belege

Diese Seite ist gegen die folgenden Primärquellen geprüft – Hersteller, Standard oder Gesetzestext. Wer es nicht glaubt, muss es nicht: Da steht, wo es nachzulesen ist.

Zuletzt geprüft am . Dokumentation altert – ein Datum daneben ist der einzige ehrliche Umgang damit.

Auch zu finden unter

Verwandte Fehler

SWLästigblocked by CORS policy

Browser blockiert Anfrage wegen CORS

Die Konsole meldet einen CORS-Fehler und die Anfrage scheitert? Nicht der Browser ist schuld — der Server muss den Zugriff von deiner Herkunft erlauben.

SWLästigMixed Content: The page at … was loaded over HTTPS, but requested an insecure … This request has been blocked; the content must be served over HTTPS.

Sichere Seite bleibt halb leer: Gemischte Inhalte werden blockiert

Die Seite läuft über HTTPS, lädt aber Bilder oder Skripte per HTTP nach. Der Browser blockiert sie — und die halbe Seite fehlt. Warum das gut ist.