Origins en cross-site verzoeken · eindproject
Eindproject: Project: verzoeken van ergens anders
Het verwijderformulier heeft nu twee eigen bewakers, een token en een controle op Origin, en één aanvalstest. Die test kan niet zeggen welke bewaker de vervalsing tegenhield. Haal een van beide weg, en hij slaagt nog steeds.
Dit project bewijst elke verdediging afzonderlijk. Bewezen heeft hier een eenvoudige betekenis: een test die nog steeds slaagt nadat je de aanvaller alles hebt gegeven wat de andere verdediging achterhoudt.
Zet een verdediging uit door die weg te geven
Er komt geen schakelaar in de server. Een test verandert wat de aanvaller in handen heeft.
Alleen het token.
test_attack_token_alone_stops_the_neighborvoegt de buur vóór al het andere toe aan de vertrouwde set. De controle opOriginlaat het vervalste formulier nu door. Het formulier draagt geen token, en het boek moet blijven.Alleen de controle op
Origin.test_attack_origin_check_alone_stops_the_neighborleest het token van Ana uit de state, wat geen pagina kan, metstate["sessions"][jar[0]["value"]]["csrf"]. Hij post het formulier met de body("id=1&csrf=" + token).encode("utf-8"). De tokencontrole slaagt nu, en het boek moet blijven.
De eerste van de twee begint zo:
def test_attack_token_alone_stops_the_neighbor():
"""The Origin check is handed to the attacker: the neighbor is trusted. The token alone must stop the forgery."""
state = reading_server.sample_state()
state["trusted_origins"].add(steps.NOTES)
Beide tests posten vanaf steps.NOTES. Beide controleren dat de
Je test voegt https://notes.reading.example toe aan state["trusted_origins"] en post daarna het vervalste formulier zonder token. Wat bewijst het als hij slaagt?
Zie elk van deze twee tests één keer falen. Zet return True als eerste regel van csrf_matches, voer python -m pytest -q uit en haal de regel weer weg. Doe daarna hetzelfde in origin_allowed. Elke run eindigt met één fout, en dat is de test voor die verdediging.
Nog twee tests maken het beeld compleet. test_the_page_can_still_delete_its_own_book post het formulier met het token vanaf steps.OWN en controleert dat boek 1 weg is. Een verdediging die ook de eerlijke pagina tegenhoudt, heeft de service kapotgemaakt. In tests/test_browser.py staat test_samesite_alone_does_not_stop_the_neighbor naast je test uit les 5: het formulier van steps.NOTES draagt de cookie. Samen leggen de twee vast wat SameSite tegenhoudt en wat niet.
Eén plek voor de controle op Origin
Het verwijderformulier is niet de enige POST die door een cookie wordt geautoriseerd. Inloggen, uitloggen, de paginagrootte en een nieuw token lezen allemaal de sessiecookie. Het projectbestand somt de vijf op:
COOKIE_POSTS = [["login"], ["logout"], ["prefs"], ["tokens"], ["books", "delete"]]
Haal de controle uit de tak voor verwijderen en maak er de eerste opdracht van route() van, direct nadat segments is gezet. Een POST waarvan de segments in COOKIE_POSTS zitten en waarvan de Origin niet is toegestaan, krijgt meteen de 403, met een
Waarom een POST /v0/books, die sinds
De controle op /login heeft een prijs, en die is een keuze: een commandline-client die geen Origin stuurt, kan niet meer inloggen.
Geen enkele GET verandert de state
test_attack_a_get_cannot_delete_a_book volgt een link vanaf een andere site. De steps.OWN + "/books/delete?id=1&csrf=" + token:
result = visit.visit(steps.OTHER, {"kind": "link", "url": url}, jar, send)
Een gevolgde link is een top-level GET, en een top-level GET, van een link of van een formulier dat met GET is verstuurd, is het enige soort cross-site verzoek dat een Lax-cookie draagt. Controleer dat de cookie meeging en dat het boek bleef. Je server antwoordt vandaag 405, omdat de route alleen POST toestaat, en deze test houdt dat zo.
Mijlpalen
Vier tests in
tests/test_attacks.pyen één intests/test_browser.py, waarvan je de eerste twee één keer hebt zien falen.De controle bovenaan
route(). Je tests kunnen deze stap niet zien: ze slagen ervoor en erna. Check my work probeert de andere routes inCOOKIE_POSTSvoor je uit.Twee runs: de tests en het scenario. Het scenario is om te lezen, en wordt niet beoordeeld.
python -m pytest -q
python pl_sec.py cross-site-delete
De eerste run eindigt met 35 passed. In de tweede wordt het formulier van de buur nu geweigerd, en dat van other.example ook, voordat er een
--- a page on https://notes.reading.example posts the same form ---
cookie sent: yes
origin: https://notes.reading.example
the server answered: 403 (pl_sec sees this; the page does not)
book ids left: 1, 2
De laatste regels zeggen:
--- result ---
0 of 2 forged forms deleted a book; the page's own form deleted its book
Rij F12 van
SECURITY.md, in je eigen woorden. De cel Test ervan noemt de vier aanvalstests van dit hoofdstuk. De status isopen: the forgery half closed in 8.8, omdat de bevinding een tweede helft heeft.
Wat nog openstaat
Die tweede helft gaat over lezen. https://app.reading.example is een tweede front-end van de leeslijst. De pagina’s daarvan zouden mogen lezen wat je server antwoordt, en vandaag houdt de
Opdracht
Bewijs elke verdediging afzonderlijk, zet de controle op Origin op één plek voor vijf routes en leg de bevinding vast.
Elke test logt ana eerst in via het model, zoals in les 5, en bereikt de code via de state["sessions"][jar[0]["value"]]["csrf"]. Het formulier is het verwijderformulier uit les 5, gepost met visit.visit(page, form, jar, send).
Schrijf in
tests/test_attacks.py, onder je test uit les 5, vier tests.test_attack_token_alone_stops_the_neighbor(): eerststate["trusted_origins"].add(steps.NOTES). Post het formulier met de bodyb"id=1"vanafsteps.NOTES. Controleer dat"cookie"in de headers van het verzoek staat, en dat1 in state["books"].test_attack_origin_check_alone_stops_the_neighbor(): post het formulier met de body("id=1&csrf=" + token).encode("utf-8")vanafsteps.NOTES, en laatstate["trusted_origins"]met. Controleer dezelfde twee dingen. test_attack_a_get_cannot_delete_a_book(): volg een link vanafsteps.OTHER,{"kind": "link", "url": steps.OWN + "/books/delete?id=1&csrf=" + token}. Controleer dezelfde twee dingen.test_the_page_can_still_delete_its_own_book(): post het formulier met het token vanafsteps.OWN, en controleer dat1 not in state["books"].
Schrijf in
tests/test_browser.pytest_samesite_alone_does_not_stop_the_neighbor(): post het formulier met de bodyb"id=1"vanafsteps.NOTESen controleer dat"cookie"in de headers van het verzoek staat.Maak in
routeinreading_server.pydit de eerste opdracht nadatsegmentsis gezet: alsmethod == "POST",segments in COOKIE_POSTSenorigin_allowed(request, state["trusted_origins"])onwaar is, geef danjson_error(403, "this request did not come from a page of this service")terug. Haal de controle uit de tak"/books/delete".Voer
python -m pytest -quit tot het eindigt met35 passed, en voer daarnapython pl_sec.py cross-site-deleteuit.Vul in
SECURITY.mdrij F12 in. De cel Test ervan noemttest_attack_another_origin_cannot_delete_a_booken de drietest_attack_-tests van deze les. De status isopen: the forgery half closed in 8.8.
Klik daarna op Check my work. Die logt een eigen gebruiker in en bestuurt je server in het geheugen, met een eigen browsermodel. Elk van de vijf routes in COOKIE_POSTS krijgt een POST met een geldige Origin ontbreekt, null is, een naburige host is of een andere site: elk moet een 403 krijgen en niets veranderen. Een POST en een PUT die door een token worden geautoriseerd, moeten nog steeds zonder Origin worden bediend, POST /v0/books moet nog steeds met 308 antwoorden, en POST /me, /about en / moeten nog steeds met 405 antwoorden. De vijf routes moeten nog steeds werken vanaf de eigen Origin, geen van beide, een GET die verwijdert, het verwijderformulier dat vanaf elke pagina wordt geweigerd, een Lax-cookie bij een cross-site formulier-POST, en een cookie die bij de buur wordt achtergehouden. Geen enkele test mag een verbinding openen. Rij F12 wordt als tabelrij gelezen; de formulering wordt niet beoordeeld. Niets uit je