Sessies die standhouden · oefening
Cookievlaggen met opzet
Je server beëindigt nu zijn
session=<id>; HttpOnly; Max-Age=3600; Path=/
Die attributen laten een browser vrij om drie dingen met de cookie te doen. Hij mag hem over gewoon HTTP sturen, waar iedereen op het netwerkpad hem kan lezen. Hij mag hem meesturen met een formulier dat een pagina op een andere site naar de leeslijst post. En hij mag een tweede cookie met dezelfde naam van een naburige host accepteren en die ook sturen, en dat was de eerste route uit les 1.
Vier instructies
Een cookieattribuut is een instructie aan de browser die de cookie bewaart. Deze les zet er vier met opzet.
| Attribuut | Wat het van een browser vraagt |
|---|---|
Secure | Stuur me alleen over HTTPS. |
HttpOnly | Laat me niet zien aan scripts in een pagina. |
SameSite=Lax | Als een pagina op een andere site het verzoek start, stuur me dan alleen mee als de browser hier een hele pagina laadt, zoals wanneer de gebruiker een link volgt, en het verzoek een GET is. Een formulier dat een andere site post, een afbeelding die ze laadt en een verzoek dat haar script doet, gaan allemaal zonder mij. |
Path=/ en geen Domain | Ik hoor bij precies deze host, en bij elk pad erop. |
“Een andere site” heeft een precieze betekenis. Een site is het schema plus het registreerbare domein, het deel van een hostnaam dat iemand geregistreerd heeft, hier reading.example. Dus https://notes.reading.example en https://reading.example zijn dezelfde site. SameSite doet dus niets tegen de naburige host uit les 1. Het prefix in het volgende deel wel, en hoofdstuk 8 behandelt verzoeken vanaf dezelfde site.
SameSite heeft nog twee Strict zou de cookie ook achterhouden als een lezer vanaf een andere site een link naar de None vraagt om helemaal geen bescherming.
Een pagina op een andere site veroorzaakt een verzoek aan de leeslijst. Wanneer voegt een browser met SameSite=Lax de cookie toe?
Het prefix __Host-
De naam van de cookie kan ook een regel dragen. Een browser weigert een cookie op te slaan waarvan de naam begint met __Host-, tenzij de cookie Secure heeft, Path=/ heeft, geen Domain heeft, en over HTTPS binnenkwam. Een cookie die een pagina op notes.reading.example voor het hele domein zet, heeft een attribuut Domain nodig, dus die kan nooit __Host-session heten. Geen enkele naburige host kan een cookie met die naam voor de leeslijst planten.
De server doet zijn deel door alleen die naam te lezen. Zodra SESSION_COOKIE "__Host-session" is, betekent een cookie met de naam session niets voor session_id_from, wie hem ook plantte.
Een pagina op notes.reading.example probeert __Host-session=evil; Domain=reading.example; Secure; Path=/ te zetten. Wat doet een browser?
Een uitzondering met pensioen
HTTP with Python liet Secure weg, in les 7.1, omdat de server daar gewoon HTTP sprak op je eigen computer. Die
pl_sec.py praat nog steeds gewoon HTTP met je server op Secure-cookie achter. Sommige browsers maken een uitzondering voor een server op je eigen computer, en die is alleen voor ontwikkeling. Het hulpprogramma is geen browser: het bouwt de header Cookie met de hand, en vanaf deze les zegt het dat direct onder zijn eerste regel:
pl_sec sends the __Host-session cookie by hand; a browser would not send it over plain HTTP
Wie gehoorzaamt
De server schrijft alleen instructies. Een test kan controleren dat ze geschreven worden. Of ze gevolgd worden, is aan de browser, en nergens op deze site controleert een browser deze vlaggen. Een programma dat geen browser is, kan elke header Cookie sturen die het wil, over elke verbinding. Hoofdstuk 8 schrijft het deel van een browser dat beslist wanneer een cookie wordt meegestuurd.
Je test slaagt: de waarde van Set-Cookie heeft elke vlag. Wat heeft de test laten zien?
De cookie waar je naartoe werkt
Met ABSOLUTE_LIMIT als standaardwaarde van max_age vergeet een browser de cookie op het moment dat de absolute limiet van de server de sessie toch zou beëindigen. Zodra de vlaggen gezet zijn, antwoordt een login met:
__Host-session=<id>; HttpOnly; Max-Age=28800; Path=/; SameSite=Lax; Secure
SimpleCookie schrijft de attributen in alfabetische volgorde, in welke volgorde je ze ook zet, en een vlag zoals Secure is een los woord zonder waarde.
Wat drukt dit af?
import http.cookies
cookie = http.cookies.SimpleCookie()
cookie["__Host-session"] = "abc"
cookie["__Host-session"]["path"] = "/"
cookie["__Host-session"]["secure"] = True
cookie["__Host-session"]["samesite"] = "Lax"
print(cookie["__Host-session"].OutputString())
Uitloggen hoeft niet te veranderen. De cookie die een sessie beëindigt, komt uit dezelfde session_cookie("", max_age=0), dus hij heeft dezelfde naam en vlaggen. Dat moet ook: een browser negeert een __Host--cookie zonder die vlaggen, en dat geldt ook voor de cookie die verwijdert.
Schrijf eerst de aanval
De aanval is een browser die doet wat de kale cookie toestaat, dus de test leest de instructies. Log in als ana via reading_server.handle(...), controleer status 200, en neem de tekst in response["headers"]["set-cookie"]. Controleer dat die begint met __Host-session=, dat Secure, HttpOnly, SameSite=Lax en Path=/ erin staan, en dat Domain er niet in staat. Voer python -m pytest -q uit: het eindigt met 1 failed, 10 passed.
Je opdracht
Schrijf in
tests/test_attacks.pytest_attack_session_cookie_carries_its_flags()en zie hem falen.Zet
SESSION_COOKIE = "__Host-session".Zet in
session_cookiesecureopTrueensamesiteop"Lax", op dezelfde manier alshttponlyenpathgezet worden, en verander de standaardwaarde vanmax_ageinABSOLUTE_LIMIT. Zet geen domein.Noem in rij F4 van
SECURITY.mdje test en zet de status opclosed in 4.4.
Voer python -m pytest -q opnieuw uit. Het eindigt met 11 passed. Je twee eerdere tests van dit hoofdstuk hoefden niet te veranderen, omdat steps.cookie_pair de naam van de cookie nooit uitschrijft.
Klik daarna op Check my work. De drie reparaties van dit hoofdstuk zijn op hun plaats. De volgende les zet ze samen als één beleid en behandelt de sessies waar niemand naar terugkomt.
Opdracht
Schrijf een test die de instructies van de
Schrijf in
tests/test_attacks.pytest_attack_session_cookie_carries_its_flags(). Log in als ana viareading_server.handle(...), controleer status 200, en neemresponse["headers"]["set-cookie"]. Controleer dat debegint met __Host-session=, dat zeSecure,HttpOnly,SameSite=LaxenPath=/bevat, en dat zeDomainniet bevat. Voerpython -m pytest -quit: deze test moet eerst falen.Zet in
reading_server.pySESSION_COOKIE = "__Host-session".Voeg in
session_cookiesecuretoe (zet het opTrue) ensamesite(zet het op"Lax"), en verander de standaardwaarde vanmax_ageinABSOLUTE_LIMIT. Zet geen domein.Voer
python -m pytest -quit: alle 11 tests slagen. Je twee eerdere tests van dit hoofdstuk hoefden niet te veranderen, omdatsteps.cookie_pairde naam van de cookie nooit uitschrijft.Noem in rij F4 van
SECURITY.mdje test en zet de status opclosed in 4.4.
Klik daarna op Check my work. Die roept session_cookie aan en splitst de tekst zelf: de naam moet __Host-session zijn, met Secure, HttpOnly, SameSite=Lax, Path=/, geen Domain, en een Max-Age van 1 tot en met ABSOLUTE_LIMIT. De volgorde en de hoofdletters van de attributen doen er niet toe, en een waarde die met een session_cookie("", max_age=0) moet dezelfde naam en vlaggen hebben. Via handle(), met een gebruiker die hij je niet heeft laten zien, logt hij in, vraagt hij GET /me met de nieuwe cookie en met een cookie met de naam session die dezelfde id bevat, die null moet antwoorden, en logt hij uit, wat de reading_server.handle en reading_server.sample_state zet: een waarvan de cookie elke vlag heeft, waar je test moet slagen; een waarvan de cookie geen Secure heeft, waar hij moet falen; en een die elke login weigert, waar hij moet falen. Als laatste leest hij rij F4 van SECURITY.md op de naam van je test en een status die begint met closed. Niets uit je