0%

Sessies die standhouden · oefening

Cookievlaggen met opzet

Je server beëindigt nu zijn . De die de id draagt, is sinds with Python niet veranderd. Een login antwoordt nog steeds met deze headerwaarde, waarin de id 22 tekens is:

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.

AttribuutWat het van een browser vraagt
SecureStuur me alleen over HTTPS.
HttpOnlyLaat me niet zien aan scripts in een pagina.
SameSite=LaxAls 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 DomainIk 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 volgt, zodat hij uitgelogd zou aankomen. Deze dienst kiest die niet. 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 eindigt hier. Een dienst die achter HTTPS gaat draaien, zet hem altijd.

pl_sec.py praat nog steeds gewoon HTTP met je server op . Over gewoon HTTP naar elke andere machine houdt een browser een 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

  1. Schrijf in tests/test_attacks.py test_attack_session_cookie_carries_its_flags() en zie hem falen.

  2. Zet SESSION_COOKIE = "__Host-session".

  3. Zet in session_cookie secure op True en samesite op "Lax", op dezelfde manier als httponly en path gezet worden, en verander de standaardwaarde van max_age in ABSOLUTE_LIMIT. Zet geen domein.

  4. Noem in rij F4 van SECURITY.md je test en zet de status op closed 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 leest, en geef de cookie daarna zijn naam en vlaggen.

  1. Schrijf in tests/test_attacks.py test_attack_session_cookie_carries_its_flags(). Log in als ana via reading_server.handle(...), controleer status 200, en neem response["headers"]["set-cookie"]. Controleer dat de begint met __Host-session=, dat ze Secure, HttpOnly, SameSite=Lax en Path=/ bevat, en dat ze Domain niet bevat. Voer python -m pytest -q uit: deze test moet eerst falen.

  2. Zet in reading_server.py SESSION_COOKIE = "__Host-session".

  3. Voeg in session_cookie secure toe (zet het op True) en samesite (zet het op "Lax"), en verander de standaardwaarde van max_age in ABSOLUTE_LIMIT. Zet geen domein.

  4. Voer python -m pytest -q uit: alle 11 tests slagen. Je twee eerdere tests van dit hoofdstuk hoefden niet te veranderen, omdat steps.cookie_pair de naam van de cookie nooit uitschrijft.

  5. Noem in rij F4 van SECURITY.md je test en zet de status op closed 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 gebouwd is, wordt geaccepteerd. 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 moet verwijderen en de cookie onder de nieuwe naam moet wissen. Hij voert je test drie keer uit, elke keer tegen een server die hij op de plaats van 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 wordt beoordeeld.