Sessies die standhouden · eindproject
Eindproject: Project: sessies die standhouden
Drie lessen deden drie reparaties: een nieuwe id bij de login, twee limieten, en een
Het beleid
| Vraag | Antwoord op de leeslijst |
|---|---|
| Wanneer begint een | Als een bezoeker een voorkeur opslaat, en bij elke login, altijd onder een nieuwe id uit secrets |
| Wat bevat ze? | user, page_size, created, last_seen |
| Wat verschuift er? | last_seen, bij elk verzoek dat de sessie levend aantreft |
| Wanneer eindigt ze? | Na IDLE_LIMIT seconden zonder verzoek, ABSOLUTE_LIMIT seconden nadat ze begon, bij het uitloggen, en bij de login, als een nieuwe sessie haar vervangt |
| Wat zegt de cookie? | __Host-session, met Secure, HttpOnly, SameSite=Lax, Path=/ en geen Domain |
| Wat mag een cache bewaren? | Niets over een sessie: elk antwoord dat een cookie zet, en elke sessieroute, zegt no-store |
Elke rij op één na is af. Het gat zit in “Wanneer eindigt ze?”.
De sessie waar niemand naar terugkomt
current_session verwijdert een verlopen sessie als haar id gebruikt wordt. Een bezoeker die nooit terugkomt, gebruikt de id nooit, dus de vermelding blijft in state["sessions"] staan zo lang als de server draait. En POST /prefs geeft een sessie aan iedereen die erom vraagt.
sweep_sessions(state) dicht het gat. Hij leest de tijd uit state["clock"](), verwijdert elke sessie waarvoor session_expired(...) waar is, en geeft terug hoeveel hij er verwijderde. start_session roept hem als eerste aan, zodat de opslag wordt opgeruimd telkens als er een sessie begint.
Eén detail bepaalt hoe je hem schrijft. Een vermelding uit een for-RuntimeError: dictionary changed size during iteration zodra de lus zijn volgende stap zet. Ruim dus in twee rondes op: verzamel eerst de verlopen id’s in een
Waarom verzamelt sweep_sessions eerst de verlopen id's in een lijst en verwijdert hij ze in een tweede lus?
Mijlpalen
1. Een drukke sessie eindigt toch. Schrijf test_attack_busy_session_still_ends(). Het is de dief uit les 3, die elke tien minuten een verzoek stuurt. Log in onder een vaste klok die bij 1000 begint. Herhaal daarna: tel 600 op bij clock["now"], vraag GET /me, en controleer dat de gebruiker ana is. Ga door zolang de volgende stap nog onder de limiet blijft:
while clock["now"] + 600 < 1000 + reading_server.ABSOLUTE_LIMIT:
Na de lus is de sessie 28200 seconden oud. Tel er 601 bij op, waarmee ze één seconde voorbij de limiet komt, vraag opnieuw, en controleer dat de gebruiker None is.
2. Uitloggen beëindigt de sessie op de server. Schrijf test_attack_logout_ends_the_session_on_the_server(). Log in met steps.login_cookie, vraag GET /me en controleer ana, stuur POST /logout met de cookie, vraag daarna GET /me met dezelfde cookie en controleer None. De aanvaller is iedereen die een kopie van de cookie van vóór het uitloggen bewaarde.
Voer python -m pytest -q uit. Het eindigt met 13 passed: beide nieuwe tests slagen de eerste keer. Les 3 bouwde de absolute limiet al, en
Je uitlogtest slaagt de eerste keer dat je hem uitvoert. Waar dient hij voor?
3. Het opruimen. Schrijf sweep_sessions(state) en roep hem aan als eerste regel van start_session. Geen enkele test van jou verandert van kleur: python -m pytest -q eindigt nog steeds met 13 passed.
4. Bekijk het beleid. Voer het sessiescenario uit:
python pl_sec.py sessions
Het logt in, laat 31 minuten verstrijken op een eigen klok, vraagt opnieuw, logt daarna in en uit en probeert de oude cookie. De id’s zijn hier ingekort:
--- a session that is left alone ---
ana logs in POST /login -> 200
set-cookie: __Host-session=<id>; HttpOnly; Max-Age=28800; Path=/; SameSite=Lax; Secure
ana asks who she is GET /me -> 200 {"user": "ana", "page_size": 20}
(the clock moves 31 minutes)
the same cookie, 31 minutes later GET /me -> 200 {"user": null, "page_size": 20}
--- a session that is logged out ---
ana logs in again POST /login -> 200
ana logs out POST /logout -> 204
set-cookie: __Host-session=""; HttpOnly; Max-Age=0; Path=/; SameSite=Lax; Secure
the cookie from before the logout GET /me -> 200 {"user": null, "page_size": 20}
--- result ---
after 31 idle minutes the session answered as: nobody
after logout the old cookie answered as: nobody
5. De bevindingen. Controleer in SECURITY.md dat de rijen F3, F4 en F5 elk een test noemen en closed zeggen.
Wat nog open staat
Opruimen begrenst hoe lang een dode sessie in de opslag blijft. Het begrenst niet hoeveel levende bezoekerssessies een vreemde in een half uur kan openen: elke POST /prefs zonder cookie maakt er een, en geen ervan verloopt binnen 30 minuten. Een echte dienst voegt een maximum toe, of wacht met een sessie maken tot hij iets heeft wat het onthouden waard is. Het opruimen leest ook elke keer dat er een sessie begint de hele opslag, dus dezelfde vloed maakt elke POST /prefs trager. Deze cursus noemt beide problemen en bouwt geen van beide oplossingen.
Klik daarna op Check my work. Je sessies houden nu stand. De bearer-tokens niet: dat zijn nog steeds twee vaste
Opdracht
Maak het sessiebeleid af.
Schrijf in
tests/test_attacks.pytest_attack_busy_session_still_ends()entest_attack_logout_ends_the_session_on_the_server()zoals de les beschrijft. Beide gebruikensteps.login_cookie(state, "ana", "correct horse"). De drukke test draait onderhelpers.fixed_clock(...), vraagt elke 600 secondenGET /meen controleert elke keeranazolang de volgende stap nog onderreading_server.ABSOLUTE_LIMITna de login blijft, zet daarna de klok op één seconde voorbij die limiet en controleertNone. De uitlogtest controleertanavóór het uitloggen enNoneerna, met dezelfde. Schrijf in
reading_server.pysweep_sessions(state): lees de tijd uitstate["clock"](), verwijder elkewaarvoor session_expired(...)waar is, en geef terug hoeveel je er verwijderde.Roep
sweep_sessions(state)aan als eerste regel vanstart_session.Voer
python -m pytest -q(13 tests slagen) enpython pl_sec.py sessionsuit.Zorg in
SECURITY.mddat de rijen F3, F4 en F5 elk een test noemen enclosedzeggen.
Klik daarna op Check my work. Die herhaalt de controles van les 2, 3 en 4 met andere kloktijden, id’s en paginagroottes: eerst de twee limieten en de klok, daarna de naam en de vlaggen van de cookie, daarna de nieuwe id bij de login en no-store. Hij roept sweep_sessions aan op een opslag met één levende, één inactieve en één te oude sessie, wat 2 moet teruggeven en de levende moet houden, en roept start_session aan op zo’n opslag, die hem moet opruimen; een opruiming die een nieuwe http_adapter, zonder een set-cookie en cache-control die terugkomen. Hij voert je vijf tests van dit hoofdstuk op naam uit tegen servers die hij op de plaats van reading_server.handle en reading_server.sample_state zet: alle vijf moeten slagen tegen een correcte server; elk moet falen tegen de ene fout waarvoor hij bedoeld is (een login die de id houdt, geen inactiviteitslimiet, geen absolute limiet, een uitlog die de sessie houdt, een cookie zonder Secure); en alle vijf moeten falen tegen een server die elke login weigert. Als laatste leest hij de rijen F3, F4 en F5 van SECURITY.md. Niets uit je