Abschlussprojekt: Härte die Leseliste · Abschlussprojekt
Abschlussprojekt: Schließ die Lücken auf dem Server
Du hast elf rote Tests und einen Server, dessen Schwächen du im Code noch nicht gesehen hast. Diese Lektion schließt die ersten neun. Sie alle liegen beim Server: in reading_server.py, signing.py und passwords.py. In den Befunden 10 und 11 geht es um Anfragen von anderen Seiten, und sie warten auf die nächste Lektion.
Die
Führ einen Test aus und lies seinen Fehlschlag.
Finde den Code, auf den der Fehlschlag zeigt.
Ändere ihn.
Führ alle Tests aus, um zu sehen, dass der gewünschte grün geworden ist und nichts anderes rot.
python -m pytest tests/test_attacks.py -q -k form_cannot_delete
-k wählt die Tests aus, deren Namen diesen Text enthalten. Ohne es laufen alle elf.
Von einem Symptom zu einer Zeile
Hier ist der ganze Weg einmal, für Befund 7. Die anderen acht sind deine.
Das Symptom ist „Another reader can delete my book with the form on the page.“ Dein Test meldet Ben an, sendet das Formular für Buch 1 mit Bens eigenem
E assert 303 == 404
Eine 303 ist die Antwort auf ein Löschen, das funktioniert hat. Die Anfrage hat also jede Prüfung auf ihrem Weg bestanden: die Methode, das Origin, die
Das Formular sendet an /books/delete. In route() findet dieser Zweig die Session und ruft delete_from_form() auf. Lies diese DELETE /books/1 mit einem Token fragt may_change() über item_refusal(). Das Formular ist ein zweiter Weg zum selben Löschen, und es hat die Frage übersprungen. Das war die Lehre aus Kapitel 6: Eine Eigentümerregel, die auf einem Weg geprüft wird und auf einem anderen nicht, ist keine Regel.
Die Korrektur ist eine
Achte darauf, was die Zeile gefunden hat. Es war keine Suche nach „Bug“. Es waren der Fehlschlag des Tests, die Reihenfolge der Prüfungen im Vertrag und eine Regel, die du schon kennst. Jeder der anderen Befunde gibt denselben drei Dingen nach. Die Regel steht in dem Kapitel, das den Befund zuerst geschlossen hat, und die Funktion, die du damals geschrieben hast, steht meist noch in der Datei, nur nicht mehr aufgerufen oder nicht mehr vollständig.
Schließ die Tür nicht für alle
Ein Befund ist geschlossen, wenn sein Angriff abgelehnt wird und jede ehrliche Anfrage weiterhin funktioniert. Die zweite Hälfte geht leicht verloren. Nimm Befund 9. Diese Änderung lässt seinen Test sofort bestehen:
def safe_cover_path(folder, name):
return None
Kein Name kann jetzt den Ordner verlassen. Es wird aber auch kein Cover ausgeliefert, und SECURITY.md sagt unter “Must still work”, dass GET /covers/kindred.txt das Cover zurückgibt. Check my work liest auch diese
Eine Änderung lässt safe_cover_path() für jeden Namen None zurückgeben. Der Test für Befund 9 besteht. Ist der Befund geschlossen?
Dieselbe Sorgfalt gilt für den Status, den eine Ablehnung bekommt. Der Vertrag antwortet 404 für ein Buch, das nicht dem Aufrufer gehört, damit die Antwort die ID nicht bestätigt, und 422 für einen Body, der ein Feld nennt, das der Client nicht setzen darf. Eine Korrektur, die 403 antwortet oder das Feld stillschweigend weglässt, schließt die Lücke und bricht den Vertrag.
Deine Aufgabe
Schließ die Befunde 1 bis 9 in der Reihenfolge des Vertrags. Führ nach jedem alle Tests aus:
python -m pytest tests/test_attacks.py -q
Die Zahl der Fehlschläge sollte jedes Mal um eins sinken:
| Nach Befund | Erwartete Fehlschläge |
|---|---|
| 1 | 10 |
| 2 | 9 |
| 3 | 8 |
| 4 | 7 |
| 5 | 6 |
| 6 | 5 |
| 7 | 4 |
| 8 | 3 |
| 9 | 2 |
Wenn die Zahl um zwei sinkt, lies den Diff noch einmal: Eine Änderung hat etwas geschlossen, das du nicht anfassen wolltest, und du willst wissen, warum. Wenn sie nicht sinkt, ist die Fehlermeldung dieses einen Tests die Stelle, an der du suchen solltest.
Zwei Hinweise dazu, wo was liegt. Die Befunde 1 und 2 sind nicht in reading_server.py: Die Arbeit einer Anmeldung passiert in passwords.py, und ein Tag wird in signing.py verglichen. Die Befunde 5 und 6 sind ebenfalls in signing.py. Alles andere ist in reading_server.py.
Nach dieser Lektion schlagen noch zwei Tests fehl. Welche, und warum ist das erwartet?
Dann drück Check my work. Die Prüfung ruft deine Funktionen und handle() mit Benutzern, Büchern, einem Schlüssel und Uhrzeiten auf, die du nicht gesehen hast, einmal für jeden der neun Befunde, und sie sieht sich nicht an, wie eine Korrektur geschrieben ist, nur, was der Code tut. Sie prüft jede Zeile von “Must still work”. Sie führt deine eigenen Tests für die Befunde 1 bis 9 gegen deine Dateien aus. Und sie führt ihre eigene Kopie der Angriffstests aus, die du in den Kapiteln 1 bis 10 geschrieben hast, weil die Testdatei in diesem Kapitel leer begonnen hat und sonst nichts diese früheren Zeilen schützt.
Aufgabe
Schließ die Befunde 1 bis 9 von SECURITY.md in reading_server.py, signing.py und passwords.py, ohne etwas unter “Must still work” kaputtzumachen.
Nach deinen Änderungen gilt:
Eine Anmeldung für einen Namen, den es nicht gibt, kostet einen Aufruf von
hashlib.scryptzu den üblichen Kosten, wie ein falsches Passwort, und bekommt dieselbe Antwort.signing.verify()entscheidet überhmac.compare_digest.Eine Anmeldung bekommt immer eine neue
. Die Session, die der Besucher hatte, wird entfernt, und ihre Seitengröße bleibt erhalten. Eine Session, die länger als
IDLE_LIMITnicht benutzt wurde, wird abgelehnt und entfernt. Eine Session, die benutzt wird, hält weiterhin bisABSOLUTE_LIMIT.Ein Token wird abgelehnt, wenn irgendein Teil seiner Nutzdaten bearbeitet wurde,
expeingeschlossen. Ein Token, das der Server erstellt hat, wird bisexpakzeptiert.signing.load_keys()löstaus, wenn READING_KEYfehlt, leer ist oder kürzer als 32 Bytes, undmain()gibt dann eine Zeile auf der Standardfehlerausgabe aus und gibt 1 zurück.Das Löschformular antwortet 404 für ein Buch, das nicht dem Aufrufer gehört, und das Buch bleibt.
Ein Buch-Body, der
owneroderidnennt, wird mit 422 beantwortet, mit dem Schlüssel in der Meldung, und nichts wird gespeichert.safe_cover_path()gibt nur für eine Datei innerhalb des aufgelösten Cover-Ordners einen Pfad zurück.
Lass die Befunde 10 und 11 und mini_browser.py für die nächste Lektion.
Führ nach jedem Befund python -m pytest tests/test_attacks.py -q aus. Die Fehlschläge gehen von 11 auf 2 zurück.
Dann drück Check my work. Die Prüfung prüft jeden der neun Befunde über handle() und deine hashlib.scrypt und hmac.compare_digest, wo es bei einem Befund um geleistete Arbeit geht. Sie prüft die Zeilen von “Must still work”: ein Passwort-Hash für eine korrekte Anmeldung, eine Session, die am Leben bleibt, solange sie benutzt wird, ein Token unter dem vorherigen Schlüssel, das eigene Löschen eines Lesers, ein Buch mit allen fünf Feldern, ein Cover und die Routen des Vertrags. Sie führt deine Tests für die Befunde 1 bis 9 gegen deine Dateien aus und ihre eigene Kopie der Angriffstests aus den Kapiteln 1 bis 10. Sie öffnet nie einen