Abschlussprojekt: Härte die Leseliste · Abschlussprojekt
Abschlussprojekt: Schließ die Cross-Origin-Lücken
Neun Befunde sind geschlossen. In den beiden roten Tests, die übrig sind, geht es um Anfragen, die eine Seite auf einer anderen Site auslöst, und dafür hast du ein Werkzeug: den Modellbrowser, den du in den Kapiteln 8 und 9 geschrieben hast. Der Vertrag sagt, dass eine seiner Regeln geschwächt wurde. Also kommt das Werkzeug zuerst.
Der Grund ist die Richtung des Fehlers. Ein Modell, das in die strenge Richtung falsch liegt, würde Anfragen ablehnen, die ein Browser sendet, und das würdest du sofort merken, weil die eigene Seite des Dienstes nicht mehr funktionieren würde. Ein Modell, das in die großzügige Richtung falsch liegt, ist leiser. Es zeigt Angriffe, die ankommen, obwohl ein echter Browser sie gestoppt hätte, und es schickt dich auf die Suche nach Abwehrmaßnahmen für Anfragen, die gar nicht passieren können, während es dir nichts Wahres über die sagt, die passieren können.
Finde die Regel
Das Modell hat eigene Tests. Führ sie aus:
python -m pytest tests/test_browser.py -q
Vier der sechs schlagen fehl, und die Zusammenfassung am Ende nennt sie:
test_lax_cookie_stays_home_on_a_cross_site_posttest_samesite_stops_a_form_from_another_sitetest_a_page_without_permission_cannot_readtest_a_delete_is_asked_about_first
Die letzten beiden sind deine Tests aus Kapitel 9, und sie scheitern an der Antwort des Servers, nicht am Modell: https://other.example wird gesagt, dass es GET /about lesen und ein DELETE senden darf. Das ist Befund 11, und er wartet bis zum Ende dieser Lektion. In den ersten beiden geht es um das Modell.
Lies den ersten. Er baut ein SameSite=Lax, fragt mini_browser.cookies_to_send(), was bei einem Top-Level-POST von https://other.example mitgeht, und erwartet nichts. Er bekommt das Cookie. Der zweite Test ist derselbe Fehler, gesehen durch visit.visit(): Ein Formular auf einer anderen Site kommt mit Anas Session-Cookie an.
cookies_to_send() fragt eine SameSite, und dort solltest du suchen. Die Regel aus Kapitel 8: Ein Cookie mit Lax geht nur dann mit einer Cross-Site-Anfrage mit, wenn die Anfrage Top-Level ist und ihre
Was zeigt das Modell mit der geschwächten Regel, wenn eine Seite auf https://other.example das Löschformular absendet?
Repariere die Regel und führ die sechs Tests noch einmal aus, bevor du weitermachst. Die beiden Modelltests bestehen; die beiden Tests aus Kapitel 9 schlagen weiterhin fehl, bis Befund 11 geschlossen ist. Pass auf, dass du nicht übers Ziel hinausschießt: Ein Cookie mit Lax geht weiterhin mit einem POST von derselben Site mit, und mit einem Link, dem man von einer anderen Site aus folgt.
Befund 10
„The delete form is accepted with a form token that was issued to a different
Überleg, wer das nutzen kann. Der Nachbar auf https://notes.reading.example ist auf derselben Site wie die Leseliste, also kommt ein Formular auf seiner Seite mit Anas Cookie an. Der Nachbar kann Anas Formular-Token nicht lesen. Aber der Nachbar kann ein eigenes Konto haben, sich anmelden und das Token auf seiner eigenen Seite lesen. Wenn der Server jedes Token akzeptiert, das er je ausgegeben hat, sendet die Seite des Nachbarn ihr eigenes Token mit Anas Cookie, und die Token-Prüfung besteht.
Dein Test für diesen Befund zeigt es: Anas Cookie, Bens Token, ein vertrauenswürdiges Origin, und das Buch ist weg. Die Prüfung in delete_from_form() fragt, ob das Token bekannt ist. Die Frage, die sie stellen muss, ist, ob das Token zu der Session gehört, die das Cookie nennt. csrf_matches(session, sent) beantwortet genau das für eine Session, mit hmac.compare_digest, und die Funktion hat diese Session schon in der Hand.
Du könntest einwenden, dass das Formular des Nachbarn sowieso gestoppt wird. Sein Origin ist https://notes.reading.example, und die Origin-Prüfung lehnt es ab, bevor das Token angesehen wird. Das stimmt heute.
Die Origin-Prüfung lehnt das Formular des Nachbarn bereits ab. Warum Befund 10 überhaupt schließen?
In Kapitel 8 hast du einen Test geschrieben, bei dem die Origin-Prüfung aus dem Weg war, und einen, bei dem das Token aus dem Weg war, um zu beweisen, dass jede Abwehr den Nachbarn allein stoppt. Befund 10 ist der Fall, in dem das Token allein niemanden stoppt, der ein Konto hat.
Befund 11
„A page on any
Dein Test sendet GET /about mit Origin: https://other.example und findet diese Origin in access-control-allow-origin wiederholt. Kapitel 9 hat das Spiegeln genannt. Es sieht sorgfältiger aus als *, weil der Header eine Origin nennt. Es ist schlimmer: Ein Browser weigert sich, * mit Zugangsdaten zu kombinieren, und gegen eine gespiegelte Origin hat er keinen solchen Einwand. Auf GET /me, wo der Dienst Zugangsdaten erlaubt, könnte also eine Seite, deren Anfragen Anas Cookie tragen, ihren Namen lesen. Mit SameSite=Lax ist das der Nachbar auf https://notes.reading.example. Eine Seite auf einer anderen Site sendet kein Cookie und liest eine Antwort, in der kein Benutzer steht. POST /tokens erlaubt ebenfalls Zugangsdaten, und dort steht nur die Origin-Prüfung zwischen dem Nachbarn und einem Token, das mit ihrem Cookie erstellt wurde.
Die Reparatur liegt in cors_headers(). Die Erlaubnis geht nur an eine Origin, die in trusted steht, als Ganzes verglichen. Dieses Set ist state["trusted_origins"], dasselbe, das die Origin-Prüfung verwendet, sodass die beiden Entscheidungen nicht auseinanderlaufen können. Behalte vary: Origin bei jeder Antwort, auch bei einer Ablehnung: Die Antwort hängt so oder so von diesem Header ab.
Deine Aufgabe
Drei Änderungen, in dieser Reihenfolge: die Regel in mini_browser.py, die Token-Prüfung im Zweig des Formulars und die cors_headers(). Dann führ alles aus:
python -m pytest -q
Das zu erwartende Ergebnis sind null Fehlschläge: elf Angriffstests und sechs Modelltests.
Dann drück Check my work. Die Prüfung ruft die Funktionen deines Modells mit Cookies und Sites auf, die du nicht gesehen hast, und führt tests/test_browser.py dagegen aus. Sie prüft Befund 10 mit zwei eigenen Sessions und Befund 11 mit einem eigenen vertrauenswürdigen Set, bei dem eine Origin, die nur so beginnt wie eine vertrauenswürdige, abgelehnt werden muss. Dann führt sie die Abfolge von Anfragen aus dem Vertrag zweimal im Speicher aus: deinen Server mit einem korrekten Modell und einen korrekten Server mit deinem Modell, damit sich ein Fehler beim einen nicht hinter dem anderen verstecken kann. Dieses Modell ist ebenfalls ein Modell; es ist kein Browser. Zuletzt wird alles aus Lektion 2 noch einmal geprüft.
Noch hat nichts einen
Aufgabe
Repariere den Modellbrowser und schließ dann die Befunde 10 und 11.
In mini_browser.py:
Führ
python -m pytest tests/test_browser.py -qaus und repariere die eine geschwächte Regel, auf dietest_lax_cookie_stays_home_on_a_cross_site_postundtest_samesite_stops_a_form_from_another_sitezeigen. Die anderen beiden Fehlschläge dort sind Befund 11. Einmit SameSite=Laxgeht nur dann mit einer Cross-Site-Anfrage mit, wenn die Anfrage Top-Level ist und ihreGET oder HEAD. Mit jeder Same-Site-Anfrage geht es weiterhin mit.
In reading_server.py:
Befund 10:
POST /books/deleteakzeptiert ein Formular-Token nur, wenn es das Token derist, die das Cookie der Anfrage nennt. Vergleich mit csrf_matches()und lies das Token nur aus dem Formular-Body. Jedes andere Token wird mit 403 beantwortet, und das Buch bleibt.Befund 11:
cors_headers(request, trusted, *, credentials=False)schreibtaccess-control-allow-originnur für eine, die in trustedsteht, als Ganzes verglichen. Diegibt bei jeder Antwort vary: Originzurück, auch wenn sie ablehnt.
Führ python -m pytest -q aus. Alle elf Angriffstests und alle sechs Modelltests bestehen.
Dann drück Check my work. Die Prüfung ruft samesite_allows() und cookies_to_send() mit Cookies und Sites auf, die du nicht gesehen hast, und führt tests/test_browser.py gegen dein Modell aus. Sie sendet das Löschformular mit dem Cookie einer Session und dem Token einer anderen Session, mit dem eigenen Token der Session und mit einem Token im Query-hmac.compare_digest auf. Sie ruft cors_headers() und handle() mit einem eigenen vertrauenswürdigen Set auf: Vertrauenswürdige Origins müssen erlaubt werden, und ein Nachbar auf derselben Site, eine Origin, die nur so beginnt wie eine vertrauenswürdige, null und ein fehlendes Origin nicht. Sie führt die Abfolge aus dem Vertrag im Speicher mit deinem Server und einem korrekten Modell aus und mit einem korrekten Server und deinem Modell. Dann wiederholt sie die Prüfungen aus Lektion 2, führt deine elf Tests gegen deine Dateien aus und ihre eigene Kopie der Angriffstests aus den Kapiteln 1 bis 10. Nichts aus deinem