Wat er mis kan gaan · oefening
Een aanval is een test die rood begint
De derde aanvaller uit les 2 heeft een kopie van de data en stuurt nooit een verzoek. Deze les laat zien wat die kopie waard is. Voer in de
python pl_sec.py stolen-backup
pl_sec.py start je eigen server op 127.0.0.1, stuurt hem een paar verzoeken en drukt af wat er gebeurde. Dit is de uitvoer, op de
pl_sec: stolen-backup, your server on 127.0.0.1 (a port the system chose)
--- two ordinary requests ---
ana logs in with her password POST /login -> 200
ana asks who she is, with a cookie GET /me -> 200 {"user": "ana"}
--- a copy of your server's state, as a thief would read it ---
books 2 books (not shown)
next_id 3
users 'ana': 'correct horse'
users 'ben': 'battery staple'
tokens 'tok-ana-writes-7Qx2': {'user': 'ana', 'can_write': True}
tokens 'tok-ben-reads-K9pz': {'user': 'ben', 'can_write': False}
sessions 'sJFKdwVWzmnyheWotZw-vA': {'user': 'ana'}
--- what the thief tries next: each stored value as that user's password ---
log in as ana with the value stored for ana -> 200 the thief is logged in
log in as ben with the value stored for ben -> 200 the thief is logged in
--- the code in reading_server.py that checks a password ---
if credentials is None or state["users"].get(credentials["user"]) != credentials["password"]:
--- result ---
2 of 2 stored values worked as a password
serve() returned after 5 connections; the server is closed
pl_sec: every thread has stopped
Het transcript lezen
Het eerste blok is gewoon gebruik: ana logt in en vraagt wie ze is. Het staat er zodat de state een levende sessie bevat.
Het tweede blok is de kopie. Die bevat beide wachtwoorden, beide tokens en de sessie-id van ana, allemaal leesbaar. In het derde blok doet de dief het eerste wat iedereen zou proberen: hij stuurt elke opgeslagen
De dief in het transcript logt in als ben. Wat had hij eerst nodig?
Twee bevindingen
F1 komt uit de twee regels van users. Exposed: het wachtwoord van elke gebruiker. Attack: iedereen met een kopie van de state leest de wachtwoorden en logt als elke gebruiker in. Fix: sla iets op wat bewijst dat een wachtwoord klopt zonder het wachtwoord te zijn. Hoofdstuk 3 bouwt dat.
F2 komt uit de geciteerde regel. De login vergelijkt de gok met de opgeslagen waarde met !=. Hoofdstuk 2, les 3 legt uit waarom die vergelijking een bevinding op zich is. Leg voorlopig de vier delen vast, zodat de rij bestaat. Exposed: hoeveel van een geraden geheim klopt. Attack: != kan sneller antwoorden bij een slechtere gok. Fix: vergelijk een geheim met hmac.compare_digest. Test: none yet.
Van een bevinding naar een test
Het vierde deel van een bevinding is haar bewijs. In deze cursus is een bewijs een aanvalstest: een test die de aanval uitvoert en controleert dat de aanval geweigerd wordt. Zolang de zwakke plek er is, faalt de test. Zodra de zwakke plek gedicht is, slaagt de test en blijft hij de wacht houden.
Voor F1 heeft de test drie stappen, de vorm arrange, act, assert die je kent, waarbij de act een aanval is:
Arrange. Bouw een state met
reading_server.sample_state(). Log daarna in als ana met haar wachtwoord en controleer dat de status 200 is. Deze eerste assert is eenvooraf. Ze zorgt ervoor dat de test gaat over een server waarop het wachtwoord van ana werkt. Aanval. Neem de kopie:
str(state)maakt van de hele state tekst, zoals een back-up dat zou doen.Controleer de weigering.
correct horsemag niet in die tekst staan.
Neem de kopie na de login. Een server zou een wachtwoord ook kunnen lekken terwijl hij het verzoek afhandelt, bijvoorbeeld in een sessie, en een kopie die eerder genomen is, zou dat missen.
tests/helpers.py bespaart je het met de hand bouwen van verzoeken. Lees nu make_request en basic. De rest van het bestand is voor latere hoofdstukken.
headers = {"Authorization": helpers.basic("ana", "correct horse")}
request = helpers.make_request("POST", "/login", headers=headers)
make_request bouwt de verzoek-handle() ontvangt, dezelfde basic(user, password) bouwt de waarde van Authorization voor Basic-inloggegevens.
Waarom logt de test eerst in en verwacht hij status 200?
Rood is vandaag de juiste kleur
Als de test geschreven is, voer je het bestand uit:
python -m pytest -q
Het eindigt met 1 failed, en pytest laat zien waarom: 'correct horse' is contained here, gevolgd door het stuk van de state dat het bevat. Die mislukking is de bevinding, herhaalbaar gemaakt. Iedereen kan nu één commando uitvoeren en F1 zien.
De test blijft rood tot hoofdstuk 3, les 6, waar je verandert hoe wachtwoorden worden opgeslagen. Het commentaar boven aan tests/test_attacks.py zegt dat, en geen enkele controle daarvoor heeft nodig dat deze test slaagt.
Je voert je nieuwe aanvalstest uit op de server zoals hij is, en hij slaagt. Wat zegt dat je?
Eén beperking, eerlijk gezegd: deze test bewijst dat geen enkele opgeslagen waarde het wachtwoord als tekst is. Een server die het wachtwoord in
Waarom via de module
Het testbestand begint met import reading_server, en je test roept reading_server.sample_state() en reading_server.handle(...) aan. Houd dat zo, en schrijf nooit from reading_server import handle.
De reden is hoe je test gecontroleerd wordt. Check my work draait je test tegen andere servers: een zwakke die gewone wachtwoorden opslaat, een gerepareerde die geen leesbaar wachtwoord bewaart, en een die elke login weigert, waarop de voorwaarde vooraf je test moet laten falen. Hij zet ze op hun plaats door reading_server.handle en reading_server.sample_state te vervangen. Een test die faalt op de zwakke server en slaagt op de gerepareerde, heeft laten zien dat hij de zwakke plek ziet. Een naam die apart geïmporteerd is, zou nog steeds naar jouw server wijzen, en de wissel zou onopgemerkt blijven. Een state-dict die je met de hand bouwt, zou ook niet bij de andere servers passen, en daarom begint de test bij sample_state().
Je opdracht
Schrijf in
tests/test_attacks.pyonder het commentaar voor deze lestest_attack_backup_reveals_no_password(), met de drie stappen hierboven.Voer
python -m pytest -quit en zie hem falen op de laatste assert.Open
SECURITY.mden vul de rijen F1 tot en met F4. Neem F1 en F2 uit deze les en F3 en F4 uit les 3, in je eigen woorden als je wilt. Houd elke rij op één regel. Zet de naam van je test in de cel Test van F1, schrijfnone yetin de andere drie, en laat elke Status opopenstaan.
Klik daarna op Check my work. Je verlaat dit hoofdstuk met vier bevindingen op papier en één daarvan bewezen door een falende test. Hoofdstuk 2 bouwt de twee gereedschappen die de oplossingen nodig hebben.
Opdracht
Schrijf de eerste aanvalstest en leg vier bevindingen vast.
Schrijf in tests/test_attacks.py de test_attack_backup_reveals_no_password():
Bouw een state met
reading_server.sample_state().Stuur
POST /loginvoor ana met haar wachtwoordcorrect horseviareading_server.handle(...), en controleer dat de status 200 is.helpers.basic("ana", "correct horse")bouwt devan Authorization, enhelpers.make_request("POST", "/login", headers=...)bouwt het verzoek.Maak daarna van de hele state tekst met
str(state)en controleer datcorrect horseer niet in staat.
Voer python -m pytest -q uit in de
Vul daarna de rijen F1 tot en met F4 van SECURITY.md: F1 en F2 uit wat python pl_sec.py stolen-backup afdrukte, F3 en F4 uit les 3. Houd één regel per rij, zet de naam van je test in de cel Test van F1, en laat elke Status op open staan.
Klik daarna op Check my work. Die draait je test vier keer, elke keer tegen een andere server die hij op de plaats van reading_server.handle en reading_server.sample_state zet: een waarvan de state gewone wachtwoorden bevat, waar je test moet falen; een die het wachtwoord van ana accepteert en er geen leesbare kopie van bewaart, waar je test moet slagen; een die het wachtwoord bij de login in de SECURITY.md: de rijen F1 tot en met F4 hebben tekst nodig in hun cellen Exposed, Attack en Fix, en F1 heeft de naam van je test nodig. De formulering van je bevindingen wordt niet beoordeeld, en niets uit je terminal wordt beoordeeld.