0%

Origins und Cross-Site-Anfragen · Übung

Welche Cookies mitgehen

Wenn Ana sich anmeldet, setzt die Antwort das , und ein Browser legt es in seinen Cookie-Speicher. Im Modell ist der Speicher eine und ein Cookie ein . Hier ist die Header-Zeile aus Kapitel 4 und das Dict, das daraus wird, mit gekürztem . Der Header ist eine Zeile, hier nach Max-Age umbrochen, damit er auf die Seite passt:

set-cookie: __Host-session=<22 characters>; HttpOnly; Max-Age=28800;
            Path=/; SameSite=Lax; Secure
{
    "name": "__Host-session",
    "value": "<22 characters>",
    "host": "reading.example",
    "path": "/",
    "secure": True,
    "samesite": "Lax",
}

host ist der Host, der das Cookie gesetzt hat. samesite ist "Strict", "Lax", "None" oder "" für ein Cookie, das keins nennt.

Bei jeder Anfrage, die eine Seite startet, fragt der Browser jedes Cookie im Speicher: Geht dieses mit? Es gibt vier Prüfungen, in dieser Reihenfolge: Host, Pfad, Secure, SameSite. Ein Cookie, das eine nicht besteht, bleibt zu Hause. cookies_to_send(jar, url, page_origin, *, method, top_level) ist diese . url ist das Ziel der Anfrage, und page_origin ist die der Seite, die sie gestartet hat.

Host

Das Cookie geht nur an genau den Host, der es gesetzt hat: cookie["host"] muss gleich urlsplit(url).hostname sein. Das Session-Cookie nennt keine Domain, also bekommt app.reading.example es nie.

Die Prüfung vergleicht Hosts, nicht Origins. Ein Cookie ist an keinen gebunden, also trägt eine Anfrage an https://reading.example:8443 die Cookies von https://reading.example.

Der Speicher enthält das Session-Cookie von reading.example. Eine Anfrage geht von einer Seite der Leseliste an https://reading.example:8443/me. Geht das Cookie mit?

Pfad

Ein Cookie geht an seinen eigenen Pfad und an jeden Pfad darunter. path_matches(cookie_path, request_path) ist in drei Fällen wahr:

  1. die beiden Pfade sind gleich;

  2. der Anfragepfad beginnt mit dem Cookie-Pfad, und der Cookie-Pfad endet auf /;

  3. der Anfragepfad beginnt mit dem Cookie-Pfad, und sein nächstes Zeichen ist /.

Also passt Path=/books zu /books/7. Es passt nicht zu /booksx, das mit denselben Buchstaben beginnt und ein anderer Pfad ist. Path=/ passt zu allem. Eine ohne Pfad, etwa https://reading.example, fragt nach /.

Ein Cookie hat Path=/books. Zu welchem Anfragepfad passt es?

Secure

Ein Secure-Cookie geht nur an eine https-URL. with Python hat Secure weggelassen, weil sein Server einfaches HTTP sprach. Kapitel 4 hat es eingeschaltet. Diese Prüfung ist die Regel, die dem Flag Bedeutung gibt.

SameSite

Zuerst fragt der Browser, ob die Anfrage innerhalb einer Site bleibt: same_site(page_origin, url), mit deiner aus Lektion 2. Wenn ja, darf jedes Cookie mitgehen. Wenn nein, entscheidet das Attribut:

  • Strict: Das Cookie bleibt zu Hause.

  • Lax: Das Cookie geht nur bei einer Top-Level-Navigation mit, deren GET oder HEAD ist. Im Modell sind ein angeklickter Link und ein abgeschicktes Formular Top-Level-Navigationen: Die Antwort wird zu der Seite, die der Leser sieht. Ein Bild und eine Anfrage, die ein Skript stellt, sind es nicht. Ein Link ist ein GET, ein Formular kann GET oder POST senden, und keine Seitennavigation verwendet HEAD. Über Sites hinweg lässt Lax das Cookie also bei einem angeklickten Link oder einem mit GET gesendeten Formular mitgehen und nie bei einem Formular-POST, einem Bild oder der Anfrage eines Skripts. Deshalb besteht Lektion 8 darauf, dass kein GET etwas ändert.

  • None: Das Cookie geht immer mit.

  • Kein Attribut: Dieses Modell behandelt das Cookie als Lax, wie Chromium.

samesite_allows(samesite, *, same_site_request, method, top_level) antwortet für ein Cookie. Das * macht die drei Namen danach zu reinen Schlüsselwortargumenten. Das mitgelieferte Set LAX_METHODS enthält "GET" und "HEAD". Vier Antworten in dieser Tabelle bleiben dir überlassen:

SameSiteDie AnfrageGeht mit?
Strictinnerhalb der Siteja
StrictCross-Site-Linknein
StrictCross-Site-Formular-POSTnein
Laxinnerhalb der Siteja
LaxCross-Site-Linkja
LaxCross-Site-Formular-POSTnein
LaxCross-Site-Bild?
NoneCross-Site-Formular-POST?
NoneCross-Site-Bildja
(fehlt)Cross-Site-Linkja
(fehlt)Cross-Site-Formular-POST?
(fehlt)Cross-Site-Bild?

Eine Seite auf https://other.example enthält ein Formular, das an https://reading.example/books/delete postet. Das Session-Cookie ist SameSite=Lax. Geht es mit?

Was ein Browser nicht speichert

Manche Cookies erreichen den Speicher nie. may_store(cookie, url) gibt für drei davon False zurück:

  • ein Secure-Cookie, das eine Antwort von einer http-URL setzt;

  • ein Cookie mit SameSite=None und ohne Secure;

  • ein Cookie, dessen Name mit __Host- beginnt und das nicht Secure ist oder dessen Pfad nicht / ist.

Kapitel 4 hat das Präfix __Host- als Versprechen gewählt. Hier ist der Code, der einen Server daran festhält.

Eine Antwort setzt __Host-session=abc; Path=/; HttpOnly ohne Secure. Was tut der Browser des Modells?

Deine Aufgabe

Schreibe in mini_browser.py die vier Funktionen, die für Lektion 3 markiert sind, in dieser Reihenfolge: path_matches, samesite_allows, may_store, cookies_to_send. Die letzte geht den Speicher durch, wendet die vier Prüfungen mit continue an und gibt eine neue Liste der Cookies zurück, die mitgehen, in der Reihenfolge des Speichers. Sie ändert den Speicher nie.

Fülle jedes Fragezeichen, indem du deine Funktion fragst. Diese Zeile fragt nach dem ersten:

python -c "import mini_browser; print(mini_browser.samesite_allows('Lax', same_site_request=False, method='GET', top_level=False))"

tests/steps.py enthält die vier Seiten-Origins als OWN, APP, NOTES und OTHER. Schreibe dann zwei Tests in tests/test_browser.py. Jeder baut einen Speicher mit einem Cookie-Dict wie dem oben auf dieser Seite und ruft mini_browser.cookies_to_send für einen Formular-POST an steps.OWN + "/books/delete" auf, mit method="POST" und top_level=True:

  • test_lax_cookie_stays_home_on_a_cross_site_post: von steps.OTHER aus ist das Ergebnis [].

  • test_lax_cookie_travels_within_the_site: von steps.NOTES aus ist das Ergebnis gleich dem Speicher.

Führe dann beide aus:

python -m pytest -q tests/test_browser.py
python mini_browser.py

Der erste endet mit 2 passed. In der Tabelle des zweiten zeigt die letzte Spalte jetzt True, True, True, False: Von den vier Seiten postet nur die auf other.example ihr Formular ohne das Cookie.

Wo sich Modell und echte Browser in dieser Lektion unterscheiden:

  • Ein fehlendes SameSite. Das Modell behandelt es als Lax, wie Chromium und wie der aktuelle Entwurf des Cookie-Standards es sagt. Chromium lässt ein solches Cookie außerdem in seinen ersten zwei Minuten bei einem Cross-Site-Top-Level-POST mitgehen, eine , die der Entwurf erlaubt. Firefox und Safari behandeln ein fehlendes SameSite als None.

  • SameSite=None ohne Secure. Chromium und Firefox weigern sich, es zu speichern, wie das Modell. Safari speichert es.

  • Das Schema in einer Site. Chromium zählt http://reading.example und https://reading.example als zwei Sites, wie das Modell. Firefox und Safari zählen sie als eine, wenn sie SameSite anwenden.

  • localhost. Chrome und Firefox akzeptieren ein Secure-Cookie von http://localhost, Safari nicht. Das Modell akzeptiert keins.

  • Das Präfix. Der Entwurf erkennt __Host- ohne Rücksicht auf Groß- und Kleinschreibung, das Modell erkennt es nur genau so.

  • Weggelassen. Das Attribut Domain, der zeitliche Ablauf, die Partitionierung von Cookies und das Blockieren von Cookies, die an Dritte gesendet werden.

Klicke dann auf Check my work.

Aufgabe

Schreibe in mini_browser.py die Regeln, die entscheiden, welche eine Anfrage trägt, und zwei Tests dazu.

Ein Cookie ist ein mit den Schlüsseln name, value, host, path, secure (True oder False) und samesite ("Strict", "Lax", "None" oder "", wenn das Cookie keins nennt). Ein Speicher ist eine solcher Dicts.

  • path_matches(cookie_path, request_path) gibt True zurück, wenn die Pfade gleich sind oder wenn der Anfragepfad mit dem Cookie-Pfad beginnt und entweder der Cookie-Pfad auf / endet oder das nächste Zeichen des Anfragepfads / ist. Andernfalls gibt sie False zurück.

  • samesite_allows(samesite, *, same_site_request, method, top_level) gibt True zurück für "None" und für jeden , wenn same_site_request wahr ist. Über Sites hinweg bekommt "Strict" False, und "Lax" und "" bekommen True nur, wenn top_level wahr ist und method in LAX_METHODS liegt.

  • may_store(cookie, url) gibt False zurück für ein Secure-Cookie, wenn url keine https- ist, für SameSite=None ohne Secure und für einen Namen, der mit __Host- beginnt, wenn das Cookie nicht Secure ist oder sein Pfad nicht / ist. Andernfalls gibt sie True zurück.

  • cookies_to_send(jar, url, page_origin, *, method, top_level) gibt eine neue Liste mit den Cookies aus jar zurück, die mit der Anfrage mitgehen, in der Reihenfolge des Speichers. Ein Cookie geht mit, wenn sein host gleich dem Host von url ist, sein Pfad zum Pfad von url passt (ein leerer Pfad zählt als /), es nicht Secure ist oder url eine https-URL ist und samesite_allows für same_site(page_origin, url) ja sagt. Der Speicher wird nicht verändert.

Schreibe in tests/test_browser.py unter dem Kommentar zu Kapitel 8 zwei Tests. Jeder ruft mini_browser.cookies_to_send mit einem Speicher auf, der ein Lax-Cookie des Hosts reading.example enthält, für einen POST an steps.OWN + "/books/delete" mit top_level=True:

  • test_lax_cookie_stays_home_on_a_cross_site_post: gestartet von einer Seite auf steps.OTHER ist das Ergebnis [].

  • test_lax_cookie_travels_within_the_site: gestartet von einer Seite auf steps.NOTES ist das Ergebnis gleich dem Speicher.

Führe python -m pytest -q tests/test_browser.py und python mini_browser.py aus.

Klicke dann auf Check my work. Es ruft die vier mit Werten auf, die du nicht gesehen hast. path_matches bekommt zehn Pfadpaare. samesite_allows bekommt jeden Wert des Attributs mit Anfragen innerhalb der Site und über Sites hinweg, mit GET, HEAD, POST und DELETE und mit beiden Werten von top_level. may_store bekommt zehn Cookies, eines davon mit __Host- mitten im Namen. cookies_to_send bekommt einen Speicher mit acht Cookies und vierzehn Anfragen: an einen tieferen Pfad, an einen Pfad, der nur mit denselben Buchstaben beginnt, an eine URL ohne Pfad, an eine http-URL, an einen anderen , an einen anderen Host der Site, und von Seiten innerhalb der Site, auf einer anderen Site und auf der http-Seite desselben Hosts. Der Speicher wird vorher und nachher verglichen. Zuletzt führt es deine zwei Tests einmal namentlich aus: Jeder muss bestehen, und jeder muss mini_browser.cookies_to_send über das aufrufen. Nichts aus deinem wird bewertet.