Hashes, MAC's en willekeurige waarden
Waarom == de verkeerde vergelijking is
Je verify antwoordt True of False, en een aanvaller die een verkeerde tag stuurt, leert alleen dat hij verkeerd was. Stel nu dat hij nog één ding leerde: hoe hard de vergelijking moest werken voordat ze False zei. Deze les laat zien wat dat extra stukje waard is. Het is de reden achter bevinding F2, die je in hoofdstuk 1 zonder reden opschreef.
Een telmodel
Hier is een vergelijking die met de hand is uitgeschreven. Ze loopt teken voor teken door twee
Elke gok behalve de laatste is verkeerd, en het antwoord is elke keer False. De telling is niet elke keer gelijk. 00000000 is fout bij het eerste teken, dus er is één teken bekeken. 7f3a0000 begint met vier juiste tekens, dus de telling is 5. Hoe beter de gok, hoe hoger de telling.
Dit is een model. De eigen == van Python op twee strings controleert eerst of ze even lang zijn en vergelijkt dan hun geheugen. Die houdt geen telling bij, en op deze site wordt niets getimed. Wat het model met het echte ding deelt, is de vorm: het werk kan afhangen van waar het eerste verschil zit.
Met het geheim 7f3a91c2, wat geeft examined("7f300000", secret) terug?
Wat de telling waard is
Een aanvaller die de telling kan zien, hoeft niet het hele geheim in één keer te raden. Hij kan het teken voor teken vinden: probeer elk van de 16 hexcijfers op de eerste positie, en houd het cijfer waarvoor de telling omhooggaat. Doe daarna hetzelfde voor de tweede positie. recover doet dat, met niets anders dan wat check meldt:
Het drukt ('7f3a91c2', 67) af: het hele geheim, na 67 gokken. Een verkeerde letter op positie p stopt de telling bij p + 1, dus een hogere telling betekent een juiste letter. Zonder de telling moet een aanvaller hele 16 ** 8, dat is 4.294.967.296. De telling maakte van één moeilijk probleem acht makkelijke. Voor een echte tag van 64 tekens heeft dezelfde
Welk geheim loopt gevaar in verify? Les 2 zei dat een tag niet geheim is. Voor een bericht dat de server ondertekende, klopt dat: de client heeft ze allebei. De juiste tag voor een bericht dat de aanvaller schreef, is iets anders. Alleen de sleutelhouder kan hem berekenen, en verify berekent hem bij elke aanroep. Als de vergelijking laat zien hoeveel van een gok klopt, kan de aanvaller die tag teken voor teken uitwerken en een vervalst bericht in handen krijgen dat geverifieerd wordt, zonder de sleutel.
Van een telling naar een klok
Een echte aanvaller heeft geen telling. Wat hij misschien wel heeft, is de tijd die een antwoord kost, omdat meer werk langer duurt. Dat signaal is piepklein en begraven in ruis, dus om het te lezen zijn enorm veel verzoeken en zorgvuldige statistiek nodig.
Je hoeft niet te beoordelen hoe praktisch de aanval tegen jouw dienst is, want de verdediging kost één regel. Dat is een patroon om vast te houden: als het dichten van een zwakke plek goedkoop is, dicht hem dan in plaats van te betogen dat niemand hem zou gebruiken.
hmac.compare_digest
De standaardbibliotheek heeft een vergelijking die voor geheimen gemaakt is:
Het drukt True, False, False af, zoals == zou doen. Het verschil zit in hoe het werkt: hmac.compare_digest bekijkt elk teken, wat de gok ook is, dus zijn werk zegt niets over waar twee waarden verschillen. Een vergelijking met die eigenschap heet een vergelijking in constante tijd (constant-time comparison).
Twee details doen ertoe als je hem gebruikt:
Hij kan nog steeds verraden dat de twee waarden een verschillende lengte hebben, en zijn documentatie zegt dat ook. Voor een tag is dat onschuldig, want iedereen weet dat een tag 64 tekens heeft.
Hij accepteert twee
str-waarden alleen als ze allebei gewone ASCII zijn, en geeft anders een. Een tag komt van een client, en een client kan alles sturen. Vergelijk dus bytes: roep eerst aan beide kanten .encode("utf-8")aan.
Wat kan hmac.compare_digest nog steeds verraden?
Waar de regel geldt
De regel is: als een geheim vergeleken wordt met één verwachte waarde, en de andere kant de gok kiest, gebruik dan hmac.compare_digest. In deze cursus is dat een tag, een wachtwoordhash in hoofdstuk 3, en nog één token in hoofdstuk 8.
Het geldt niet voor elke opzoeking. state["sessions"].get(session_id) vindt een != van de login op een wachtwoord en niet over de sessieopslag.
Welke regel heeft hmac.compare_digest nodig?
Waarom sleutels uit secrets komen
Nog één los eind uit les 2: waarom secrets.token_bytes, en niet de random?
De twee regels zijn dezelfde 32 tekens, en de laatste regel drukt True af. random is gebouwd om herhaalbaar te zijn: vanaf hetzelfde beginpunt, de seed, maakt hij altijd dezelfde waarden. Voor een simulatie of een spel is dat een voordeel. Voor een sleutel is het fataal, want iedereen die de seed te weten komt, of genoeg van de uitvoer ziet, kan berekenen wat er daarna komt. secrets vraagt het
Een programma roept random.seed(2026) aan en daarna random.randbytes(16).hex(). Morgen wordt het opnieuw uitgevoerd. Wat drukt de tweede run af?
Je hebt nu de reden voor F2 en het gereedschap dat hem oplost. Het project in de volgende les zet hmac.compare_digest in verify en bewijst het met tests.