Eindproject: maak de leeslijst veilig · eindproject
Eindproject: Dicht de gaten aan de serverkant
Je hebt elf rode tests en een server waarvan je de zwakke plekken nog niet in de code hebt gezien. Deze les dicht de eerste negen. Ze zitten allemaal aan de kant van de server: in reading_server.py, signing.py en passwords.py. Bevindingen 10 en 11 gaan over verzoeken van andere pagina’s, en die wachten op de volgende les.
De
Voer één test uit en lees zijn fout.
Zoek de code waar de fout naar wijst.
Verander die.
Voer alle tests uit, om te zien dat de test die je wilde groen werd en dat niets anders rood werd.
python -m pytest tests/test_attacks.py -q -k form_cannot_delete
-k selecteert de tests waarvan de naam die tekst bevat. Zonder die optie draaien ze alle elf.
Van een symptoom naar een regel
Hier is het hele pad één keer, voor bevinding 7. De andere acht zijn voor jou.
Het symptoom is “Another reader can delete my book with the form on the page.” Je test logt Ben in, post het formulier voor boek 1 met Bens eigen
E assert 303 == 404
Een 303 is het antwoord op een verwijdering die gelukt is. Het verzoek kwam dus onderweg door elke controle: de methode, de Origin, de
Het formulier post naar /books/delete. In route() vindt die tak de sessie en roept hij delete_from_form() aan. Lees die DELETE /books/1 met een token vraagt het aan may_change() via item_refusal(). Het formulier is een tweede pad naar dezelfde verwijdering, en het sloeg die vraag over. Dat was de les van hoofdstuk 6: een eigenaarsregel die op het ene pad wordt gecontroleerd en op het andere niet, is geen regel.
De oplossing is één
Let op wat de regel vond. Het was geen zoektocht naar “bug”. Het was de fout van de test, de volgorde van de controles in het contract, en één regel die je al kent. Elk van de andere bevindingen geeft zich gewonnen aan diezelfde drie dingen. De regel staat in het hoofdstuk dat de bevinding voor het eerst dichtte, en de functie die je toen schreef, staat meestal nog in het bestand, alleen wordt ze niet meer aangeroepen of is ze niet meer compleet.
Sluit de deur niet voor iedereen
Een bevinding is gedicht als haar aanval wordt geweigerd en elk eerlijk verzoek nog werkt. De tweede helft raak je makkelijk kwijt. Neem bevinding 9. Deze wijziging laat haar test meteen slagen:
def safe_cover_path(folder, name):
return None
Geen naam kan de map nu nog verlaten. Er wordt ook geen cover meer geserveerd, en SECURITY.md zegt onder “Must still work” dat GET /covers/kindred.txt de cover teruggeeft. Check my work leest die
Een wijziging laat safe_cover_path() voor elke naam None teruggeven. De test voor bevinding 9 slaagt. Is de bevinding gedicht?
Dezelfde zorg geldt voor de status die een weigering krijgt. Het contract antwoordt 404 voor een boek dat niet van de aanvrager is, zodat het antwoord de id niet bevestigt, en 422 voor een body die een veld noemt dat de client niet mag zetten. Een oplossing die 403 antwoordt, of die het veld stilletjes laat vallen, dicht het gat en breekt het contract.
Je opdracht
Dicht bevindingen 1 tot en met 9, in de volgorde van het contract. Voer na elke bevinding alle tests uit:
python -m pytest tests/test_attacks.py -q
Het aantal fouten moet elke keer met één dalen:
| Na bevinding | Te verwachten fouten |
|---|---|
| 1 | 10 |
| 2 | 9 |
| 3 | 8 |
| 4 | 7 |
| 5 | 6 |
| 6 | 5 |
| 7 | 4 |
| 8 | 3 |
| 9 | 2 |
Als het aantal met twee daalt, lees de diff dan opnieuw: één wijziging dichtte iets wat je niet wilde aanraken, en je wilt weten waarom. Als het niet daalt, is de foutmelding van die ene test de plek om te kijken.
Twee hints over waar dingen staan. Bevindingen 1 en 2 zitten niet in reading_server.py: het werk van een login gebeurt in passwords.py, en een tag wordt vergeleken in signing.py. Bevindingen 5 en 6 zitten ook in signing.py. Al het andere zit in reading_server.py.
Na deze les falen er nog twee tests. Welke, en waarom is dat te verwachten?
Druk daarna op Check my work. Die roept je functies en handle() aan met gebruikers, boeken, een sleutel en kloktijden die je niet hebt gezien, één keer voor elk van de negen bevindingen, en kijkt niet naar hoe een oplossing is geschreven, alleen naar wat de code doet. Hij controleert elke rij van “Must still work”. Hij voert je eigen tests voor bevindingen 1 tot en met 9 uit tegen je bestanden. En hij voert zijn eigen kopie uit van de aanvalstests die je in hoofdstuk 1 tot en met 10 schreef, omdat het testbestand in dit hoofdstuk leeg begon en niets anders die eerdere rijen bewaakt.
Opdracht
Dicht bevindingen 1 tot en met 9 van SECURITY.md in reading_server.py, signing.py en passwords.py, zonder iets onder “Must still work” kapot te maken.
Na je wijzigingen:
Kost een login voor een naam die niet bestaat één aanroep van
hashlib.scryptmet de gebruikelijke kosten, net als een verkeerd wachtwoord, en krijgt hij hetzelfde antwoord.Beslist
signing.verify()viahmac.compare_digest.Krijgt een login altijd een nieuwe
. De sessie die de bezoeker had, wordt verwijderd, en de paginagrootte ervan blijft bewaard. Wordt een sessie die langer dan
IDLE_LIMITniet is gebruikt, geweigerd en verwijderd. Een sessie die in gebruik is, duurt nog steeds totABSOLUTE_LIMIT.Wordt een token geweigerd als een deel van zijn payload is bewerkt,
expinbegrepen. Een token dat de server heeft gemaakt, wordt geaccepteerd totexp.Werpt
signing.load_keys()op als READING_KEYontbreekt, leeg is of korter is dan 32 bytes, en druktmain()dan één regel af op standaardfout en geeft het 1 terug.Antwoordt het verwijderformulier 404 voor een boek dat niet van de aanvrager is, en blijft het boek staan.
Krijgt een boekbody die
ownerofidnoemt het antwoord 422 met de sleutel in de melding, en wordt er niets opgeslagen.Geeft
safe_cover_path()alleen een pad terug voor een bestand binnen de opgeloste covermap.
Laat bevindingen 10 en 11, en mini_browser.py, liggen voor de volgende les.
Voer na elke bevinding python -m pytest tests/test_attacks.py -q uit. Het aantal fouten gaat van 11 naar 2.
Druk daarna op Check my work. Die controleert elk van de negen bevindingen via handle() en je hashlib.scrypt en hmac.compare_digest waar een bevinding over verricht werk gaat. Hij controleert de rijen van “Must still work”: één wachtwoordhash voor een correcte login, een sessie die in leven blijft zolang ze wordt gebruikt, een token onder de vorige sleutel, een lezer die zijn eigen boek verwijdert, een boek met alle vijf velden, een cover, en de routes van het contract. Hij voert je tests voor bevindingen 1 tot en met 9 uit tegen je bestanden, en zijn eigen kopie van de aanvalstests uit hoofdstuk 1 tot en met 10. Hij opent nooit een